Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Use of decltype

96 views
Skip to first unread message

Paul

unread,
Mar 11, 2016, 6:56:48 AM3/11/16
to
Suppose I want to sort a vector in reverse order, using std::greater. I use decltype because I don't want to explicitly state the type but I want to select the type of the first element.

The code snippet is below:

std::vector<int> v{1};
std::sort(v.begin(), v.end(), std::greater<decltype(v[0])>());

Why does this fail to compile? If v[0] is replaced by 5, it does compile. My thinking is that decltype(v[0]) means same type as v[0] which means same type as 1 which is int and that therefore std::sort(v.begin(),v.end(), std::greater<int>()); should be executed.

Where am I going wrong? What should be done in practice if the type of v[0] is not simply int but a very complex type, and I want to avoid repeating the name of the type? I suppose typedef is the solution, but I thought decltype was used to solve this type of problem, too.

Many thanks for your help.

Paul

Victor Bazarov

unread,
Mar 11, 2016, 7:37:49 AM3/11/16
to
It's possible that v[0] gives you not 'int', but 'int&', I'm too lazy to
check now. If you wanted to use 'v' and 'decltype' to supply 'int' to
instantiate 'greater', you could try

... std::greater<decltype(v)::value_type> ...

V
--
I do not respond to top-posted replies, please don't ask

Paul

unread,
Mar 11, 2016, 8:18:19 AM3/11/16
to
Yes, this works. Thanks.

Paul

Juha Nieminen

unread,
Mar 11, 2016, 2:31:47 PM3/11/16
to
Paul <peps...@gmail.com> wrote:
> std::vector<int> v{1};
> std::sort(v.begin(), v.end(), std::greater<decltype(v[0])>());

I haven't tried, but this probably works:

std::greater<std::remove_reference_t<decltype(v[0])>>

Of course this is becoming needlessly verbose. Just use the vector's
value_type.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Alf P. Steinbach

unread,
Mar 11, 2016, 5:01:17 PM3/11/16
to
On 11.03.2016 12:56, Paul wrote:
> Suppose I want to sort a vector in reverse order, using std::greater.
> I use decltype because I don't want to explicitly state the type but
> I want to select the type of the first element.
>
> The code snippet is below:
>
> std::vector<int> v{1}; std::sort(v.begin(), v.end(),
> std::greater<decltype(v[0])>());
>
> Why does this fail to compile?

Sounds like a reference to me.

Why not try

decltype( +v[0] )

Disclaimer: I have not checked that.

Also there was something about double parenthesis.

They crammed too much into one thing, old g++ typeof was more practical.


Cheers!,

- Alf

woodb...@gmail.com

unread,
Mar 11, 2016, 6:09:43 PM3/11/16
to
On Friday, March 11, 2016 at 4:01:17 PM UTC-6, Alf P. Steinbach wrote:
> On 11.03.2016 12:56, Paul wrote:
> > Suppose I want to sort a vector in reverse order, using std::greater.
> > I use decltype because I don't want to explicitly state the type but
> > I want to select the type of the first element.
> >
> > The code snippet is below:
> >
> > std::vector<int> v{1}; std::sort(v.begin(), v.end(),
> > std::greater<decltype(v[0])>());
> >
> > Why does this fail to compile?
>
> Sounds like a reference to me.
>
> Why not try
>
> decltype( +v[0] )
>

I think Victor's solution is clearer.

The syntax you suggest reminds me of Perl - cryptic.

Brian
Ebenezer Enterprises - If you can't join 'em, beat 'em.
http://webEbenezer.net

Paul

unread,
Mar 12, 2016, 4:09:32 AM3/12/16
to
Thanks. The actual problem was experienced with a std::vector<std::pair<std::string, int> >; However, I tried to find and post the simplest example of the anomalous (to me) behaviour. I tried to post my question (as opposed to the example) in an abstract way to encompass a variety of types. With the above pair example, + doesn't work because there's no such operator. I like the value_type solution. typedef is fine too, but I have trouble remembering whether it should be typedef int PseudonymForInt; or typedef PseudonymForInt int; (The first is correct, I think.

Thanks to all on this thread.

Paul

Öö Tiib

unread,
Mar 12, 2016, 7:03:17 AM3/12/16
to
On Saturday, 12 March 2016 11:09:32 UTC+2, Paul wrote:

> typedef is fine too, but I have trouble remembering whether it should
> be typedef int PseudonymForInt; or typedef PseudonymForInt int;
> (The first is correct, I think.

The 'using' alias syntax is slightly more intuitive than 'typedef'.
See:

using Name = std::string;
using Weight = int;
using NameWeight = std::pair<Name, Weight>;
using NameWeights = std::vector<NameWeight>;


NameWeights x{{"foo",42},{"bar",13}};
std::sort(begin(x), end(x), std::greater<NameWeight>());

Also if those constructs with 'decltype' and 'value_type' (and
'remove_reference' and 'const_reverse_iterator' etc.) turn code lines
into too verbose then use local alias:

template<typename Container>
void reverse_stable_sort(Container& c)
{
using Element = typename Container::value_type;
std::stable_sort(begin(c), end(c), std::greater<Element>());
}

Juha Nieminen

unread,
Mar 14, 2016, 5:16:54 AM3/14/16
to
Paul <peps...@gmail.com> wrote:
> typedef is fine too, but I have trouble remembering whether it should be
> typedef int PseudonymForInt; or typedef PseudonymForInt int; (The first
> is correct, I think.

It's rather easy. It uses the exact same principle as declaring a variable.
For example, if you want a variable named "foo" of type int, you write:

int foo;

To make it a type alias, simply add 'typedef' at the beginning:

typedef int foo;

This same idea works with more complex types as well. For example, if you
are declaring an array of 3 ints named 'foo', it would be:

int foo[3];

Add 'typedef' at the beginning, and it becomes a type alias for such a thing:

typedef int foo[3];

If you are declaring, let's say, a function pointer to int(double) named "foo",
it would be:

int(*foo)(double);

Now add 'typedef' at the beginning and it becomes a type alias:

typedef int(*foo)(double);

(Now 'foo' is a type alias for a function pointer to a function of type
int(double).)

woodb...@gmail.com

unread,
Mar 14, 2016, 2:20:43 PM3/14/16
to
On Monday, March 14, 2016 at 4:16:54 AM UTC-5, Juha Nieminen wrote:
>
> It's rather easy. It uses the exact same principle as declaring a variable.
> For example, if you want a variable named "foo" of type int, you write:
>
> int foo;
>
> To make it a type alias, simply add 'typedef' at the beginning:
>
> typedef int foo;
>
> This same idea works with more complex types as well. For example, if you
> are declaring an array of 3 ints named 'foo', it would be:
>
> int foo[3];
>
> Add 'typedef' at the beginning, and it becomes a type alias for such a thing:
>
> typedef int foo[3];
>
> If you are declaring, let's say, a function pointer to int(double) named "foo",
> it would be:
>
> int(*foo)(double);
>
> Now add 'typedef' at the beginning and it becomes a type alias:
>
> typedef int(*foo)(double);
>
> (Now 'foo' is a type alias for a function pointer to a function of type
> int(double).)
>

I think the using approach is clearer than the typedef
approach. And from a code generator perspective the
using approach is easier to support.


Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net

Juha Nieminen

unread,
Mar 15, 2016, 5:03:45 AM3/15/16
to
woodb...@gmail.com wrote:
> I think the using approach is clearer than the typedef
> approach. And from a code generator perspective the
> using approach is easier to support.

It's still good to understand how typedef works.

SG

unread,
Mar 16, 2016, 9:34:52 AM3/16/16
to
On Friday, March 11, 2016 at 12:56:48 PM UTC+1, Paul wrote:
> Suppose I want to sort a vector in reverse order, using std::greater. I use decltype because I don't want to explicitly state the type but I want to select the type of the first element.
>
> The code snippet is below:
>
> std::vector<int> v{1};
> std::sort(v.begin(), v.end(), std::greater<decltype(v[0])>());
>
> Why does this fail to compile? If v[0] is replaced by 5, it does compile. My thinking is that decltype(v[0]) means same type as v[0] which means same type as 1 which is int and that therefore std::sort(v.begin(),v.end(), std::greater<int>()); should be executed.

decltype is kind of overloaded. IIRC there are three cases to consider:

(1) It gives you the *declared* type of some item.
(2) It gives you the return type of whatever function you called.
(3) It gives you the type and "lvalueness" of an arbitrary expression:
T& for lvalue expressions of type T,
T for rvalue expressions of type T

An extra set of parentheses surrounding decltype's argument basically
forces case 3.

Examples:

struct S { int m; }
int i = 42;
int& L = i;
int&& R = std::move(i);
const S s = { 99 };
std::vector<double> vec = { 3.14 };

// Examples for case 1: "declared type"
decltype(i) // int
decltype(L) // int&
decltype(R) // int&&
decltype(s.m) // int (S::m is not declared const)

// Examples for case 3: "type and lvalueness of expression"
decltype(+i) // int
decltype((i)) // int&
decltype((L)) // int&
decltype((R)) // int&
decltype((s.m)) // const int& (s is const, so is s.m)
decltype((std::move(i))) // int

// Examples for case 2: "return type of function"
decltype(std::move(i)) // int&&
decltype(std::rand()) // int
decltype(vec[0]) // double&


> Where am I going wrong? What should be done in practice if the type of v[0] is not simply int but a very complex type, and I want to avoid repeating the name of the type? I suppose typedef is the solution, but I thought decltype was used to solve this type of problem, too.

You could use

typename std::decay<decltype(v[0])>::type // C++11, or
std::decay_t<decltype(v[0])> // C++14

The type transformation trait std::decay basically gets rid of
references as well as const and volatile. std::decay_t is a templated
type alias since C++14 which makes this shorter. But if you're OK
with reling on C++14, you could just as well write

std::sort(v.begin(), v.end(), std::greater<>());

This creates a std::greater<void> which is specialized to have a
generic function call operator since C++14. :-)


Cheers!
sg
0 new messages