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.
The relevant standards quote is:
N4140 [dcl.type.simple]/4:
For an expressione
, the type denoted bydecltype(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 bye
. 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)
isT&&
, whereT
is the type of e;- otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type of e;- otherwise,
decltype(e)
is the type ofe
.
Since x
is an lvalue, and the expression is parenthesized, the third rule is used, so decltype((x))
is int&
.
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.
§ 7.1.6.4 [dcl.spec.auto] (draft n3797)
- ... 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]
- 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&
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With