'C++11 why the type of 'decltype(x)' and 'decltype((x))' are different?

I found they're different, and the language standard says what kind of type each statement should retrieve(difference between variable and expression). But I really wish to know why these 2 kinds of types should be different?

#include<stdio.h>
int x=0;
decltype((x)) y=x;
int main()
{
    y=2;
    printf("%d,",x);
    decltype((1+2))&z=x;//OK (1+2) is an express, but why decltype should differ?
    z=3;
    printf("%d\n",x);
    return 0;
}

The running result is '2,3'

So why decltype((int)) is int& by design, what's the consideration of C++ language design here? Any syntax consistency that requires such a design? (I don't wish to get "This is by design")

Thanks for your explanations.



Solution 1:[1]

If you read e.g. this decltype reference you will see

2) If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, ...

3) If the argument is any other expression...

... b) if the value category of expression is lvalue, then decltype yields T&;

[Emphasis mine]

And then a little further down the note

Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types.

Because you use a parenthesized expression it is treated as an lvalue, meaning that 3.b above is active and decltype((x)) gives you int& if x is int.

It should be noted that while the reference isn't authoritative it is derived from the specification and generally reliable and correct.


From the C++11 specification ISO/IEC 14882:2011, section 7.1.6.2 [dcl.type.simple], sub-section 4:

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

And with an example:

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

Basically exactly what the previously linked reference said.

With your example, e in the specification is (x) (since you have declspec((x))). Now the first case doesn't fit because (x) is not an unparenthesized expression. The second case doesn't fit because (x) isn't an xvalue. The third case matches though, (x) is an lvalue of type int, leading decltype((x)) to be int&.

So the answer to your query is simply: Because the specification says so.

Solution 2:[2]

Well, The answer I see here is that "the specification says so". I looked into stroustrup's original drafts of decltype and this is what it says.

if expr in decltype(expr) is a variable or formal parameter the programmer can trace down the variable’s or parameter’s declaration, and the result of decltype is exactly the declared type. If expr is a function invocation, the programmer can perform manual overload resolution; the result of the decltype is the return type in the prototype of the best matching function. The prototypes of the built-in operators are defined by the standard, and if some are missing, the rule that an lvalue has a reference type applies.

Look at the last statement here, I think that explains. Since parenthesis are built-in operators to indicate and expression.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 sanjivgupta