49

I was reading this article on Wikipedia regarding C++11 Type Inference feature.

There is an example and I quote:

#include <vector>
int main() {  
    const std::vector<int> v(1);  
    auto a = v[0];        // a has type int  
    decltype(v[1]) b = 1; // b has type const int&, the return type of  
                          //   std::vector<int>::operator[](size_type) const  
    auto c = 0;           // c has type int  
    auto d = c;           // d has type int  
    decltype(c) e;        // e has type int, the type of the entity named by c  
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue  
    decltype(0) g;        // g has type int, because 0 is an rvalue  
}

in the following lines:

    decltype(c) e;        // e has type int, the type of the entity named by c  
    decltype((c)) f = c;  // f has type int&, because (c) is an lvalue  

What's the difference between c and (c)? Why does (c) represent an lvalue?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
MBZ
  • 26,084
  • 47
  • 114
  • 191

2 Answers2

28
  • c is the name of a variable;

  • (c) is an expression, in this case an lvalue expression, whose value is identical to the value of the variable c.

And the two are treated differently by decltype. Consider, for example, decltype(1+2), which is also an example taking an expression. It just so happens that your example is a simple version of an expression: one which merely names a single variable and does nothing exciting with it.

It's one of those differences that you generally only really care about if you're rationalising about the subtle parts of the language specification; though, as you have identified, it has quite a significant practical effect in this case.

Please note though that there is no operator usage here. It's all simply a deduction from the layout of the grammar.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Can you explain why decltype(1+2) shouldn't be `const int` for example? – dchhetri Jan 02 '13 at 01:10
  • 1
    @user814628 why would it be? Addition doesn't make it `const`. – Seth Carnegie Jan 02 '13 at 01:19
  • I guess for me it feels more natural for (1+2) to be constant because its a temporary result. – dchhetri Jan 02 '13 at 01:26
  • [Temporaries are not inherently constant](http://stackoverflow.com/questions/6466253/if-temporaries-are-implicitly-non-modifiable-how-does-this-work). However, you cannot mutate a literal and I think actually that `decltype(1+2)` is `int`; I worry therefore that I may have made an error in judgment in stepping into this area of C++11 for this answer, as I'm no `decltype` expert. – Lightness Races in Orbit Jan 02 '13 at 01:27
  • see also: http://stackoverflow.com/questions/2169932/non-class-rvalues-always-have-cv-unqualified-types – Lightness Races in Orbit Jan 02 '13 at 01:29
  • 2
    Isn't it `c` also an expression? – Jean-Baptiste Yunès Sep 04 '19 at 13:27
  • 2
    @Jean-BaptisteYunès Yes. `decltype` simply has a special case: `decltype(expr)` gives the type and value category of the expression `expr` *except* if the expression is an unparenthesized identifier (+ some other exceptions), in which case it does something different (gives the `decl`ared `type` of the variable instead of the type and category the expression would have in any other context). – HTNW Jul 12 '20 at 09:29
12

I found a good description here. It describes the difference between:

struct A { double x; };
const A* a = new A();
...
decltype(a->x) x4; // type is double
decltype((a->x)) x5; // type is const double&

and I quote:

The reason for the difference between the latter two invocations of decltype is that the parenthesized expression (a->x) is neither an id-expression nor a member access expression, and therefore does not denote a named object. [13]

Because the expression is an lvalue, its deduced type is "reference to the type of the expression", or const double&. [10]

Community
  • 1
  • 1
MBZ
  • 26,084
  • 47
  • 114
  • 191