21

Given:

decltype(auto) f1()
{
    int x = 0;
    return x;  // decltype(x) is int, so f1 returns int
}
decltype(auto) f2()
{
    int x = 0;
    return (x);  // decltype((x)) is int&, so f2 returns int&
}

(Taken from Scott Meyer's Effective Modern C++).

Now, if I have found the correct paragraph, Section 7.1.5.2 Simple type specifiers [dcl.type.simple] of the C++ standard says:

If e is an id-expression or a class member access (5.2.5 [expr.ref]), decltype(e) is defined as the type of the entity named by e

and the example from that section is:

struct A { double x; }

const A* a = new A();

decltype((a->x)); // type is const double&

Now, I wonder why is the decltype((x)) is deduced to be int& in the book.

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • 2
    why do you need the decltype for? auto is enough.. – David Haim Nov 02 '15 at 14:15
  • works fine without the decltype : http://coliru.stacked-crooked.com/a/0cb0832e241a967e – David Haim Nov 02 '15 at 14:20
  • 13
    but let's face it, the answer will probably be someone quoting from standard some weird-ass neuance of some esoteric rule which makes it behave like it.. – David Haim Nov 02 '15 at 14:21
  • 2
    @DavidHaim added the tag for weird-ass nuances of esoteric rules. – Quentin Nov 02 '15 at 14:26
  • Scott Mayer explained it - `(x)` is an expression, and expressions 'return' references (think `(x+=0)`) – Hcorg Nov 02 '15 at 14:27
  • What else could `decltype((x))` be? Did you expect anything else than `int&`? If yes, please specify what. It seems pretty obvious - the book and the Standard agree - so you might want to specify what exactly is surprising/unclear here. – anatolyg Nov 02 '15 at 14:28
  • @Quentin This is exactly what "Language Lawyer" stands for, LOL! – David Haim Nov 02 '15 at 14:29
  • @anatolyg: In the same vein I could say that it seems pretty obvious he expected `int`. What else? You follow the rules of the standard, I follow the rules of basic human reasoning. – Karoly Horvath Nov 02 '15 at 14:33
  • 1
    @DavidHaim While what you describe does happen frequently on this site, I would hardly call this quite non corner case use of a high profile C++11 feature a "weird-ass neuance of some esoteric rule". – Baum mit Augen Nov 02 '15 at 14:36
  • 7
    See [decltype and parentheses](http://stackoverflow.com/q/3097779/1708801) – Shafik Yaghmour Nov 02 '15 at 14:41
  • 1
    @BaummitAugen , well, I was sarcastic and vulgar, true, but I dought anyone will intentially write the mixture of `decltype(auto)` with `return (something)`, especially when `auto` + `return x` works good enough – David Haim Nov 02 '15 at 14:41
  • @DavidHaim but `auto` + `return x;` returns a `int`, not a `int&`. – TartanLlama Nov 02 '15 at 15:02
  • ... which is exactly what he expected to get.. – David Haim Nov 02 '15 at 15:03
  • See http://stackoverflow.com/a/20721887/576911 for a tool `expression_name` that is handy for exploring this issue. – Howard Hinnant Nov 02 '15 at 15:22

5 Answers5

10

The relevant standards quote is:

N4140 [dcl.type.simple]/4: For an expression e, the type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  • otherwise, decltype(e) is the type of e.

Since x is an lvalue, and the expression is parenthesized, the third rule is used, so decltype((x)) is int&.

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
8

decltype returns the declared type of a variable, or the "type" of an expression (with some reference added to indicate l/r valueness).

This lets it be used for two different purposes. Sometimes this causes confusion, but it is what it is.

The token x is a variable. The type of the variable is int.

The tokens (x) are not a variable, but rather a (really trivial) expression containing nothing but one variable. As such, the type of the expression (as determined by decltype) (x) is int&.

The type of the expression x (if you could convince decltype to give it to you; you cannot) is also int&, but the rule that the decltype(ACTUAL_VAR_NAME) evaluates to the type of the variable "wins".

Now, none of the above is true. The actual truth is a quote of the standard which describes the steps that a compiler is supposed to go through to determine what type decltype returns. But it is an effective lie, and one (if the standard wording turned out to have errors) that might indicate the standard has a bug when it disagrees with it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
5

§ 7.1.6.4 [dcl.spec.auto] (draft n3797)

  1. ... If the placeholder is the decltype(auto) type-specifier , the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in 7.1.6.2 , as though the initializer had been the operand of the decltype .

§ 7.1.6.2 [dcl.type.simple]

  1. For an expression e , the type denoted by decltype(e) is defined as follows:

— if e is an unparenthesized id-expression or an unparenthesized class member access ( 5.2.5 ), decltype(e) is the type of the entity named by e . If there is no such entity, or if e names a set of overloaded func- tions, the program is ill-formed;

— otherwise, if e is an xvalue, decltype(e) is T&& , where T is the type of e ;

— otherwise, if e is an lvalue, decltype(e) is T& , where T is the type of e ;

— otherwise, decltype(e) is the type of e

x is is an unparenthesized id-expression and therefore the return type is deduced as the type of x: int

(x) is not an unparenthesized id-expression so that rule does not apply. However, it is a (parenthesized) lvalue expression. Therefore the deduced type is T& where T is the type of x: int&

eerorika
  • 232,697
  • 12
  • 197
  • 326
5

[dcl.spec.auto]/7 mandates that, roughly speaking, the return type is obtained by applying decltype to the expression in the return statement. Thus, as the comment suggests, we're looking for decltype((x)). A fiendish rule steps in:

enter image description here

Note that the first bullet point does not apply since the expression is parenthesized. Hence we get int&.


This distinction between applications of decltype on plain identifiers and parenthesized ones was introduced with revision 6 of the corresponding paper. See in revision 5, §2.3:

The type denoted by decltype(e) is defined as follows:

  1. If e is of the form (e1), decltype(e) is defined as decltype(e1).

The justification behind this is presumably the following: Writing (x), the programmer intends decltype to not treat the operand as a name but rather an expression - considering its value category.

Community
  • 1
  • 1
Columbo
  • 60,038
  • 8
  • 155
  • 203
0

As @Columbo has already pointed out, (a->x) is to be taken as an expression, and that expression is const qualified, because *a is const.

Let's consider this

decltype((a->x)); 

was not const qualified.

Then e.g. this

decltype((a->x)) ref = (a->x); 

would implicitly sort-of lift the *a const qualifier, because *a's x would become mutable through ref.

decltype_auto
  • 1,706
  • 10
  • 19