C++ decltype and parentheses - why?

Why are parentheses treated differently?

Parentheses aren't treated differently. It's the unparenthesized id-expression that's treated differently.

When the parentheses are present then the regular rules for all expressions apply. The type and value category are extracted and codified in the type of decltype.

The special provision is there so that we could write useful code more easily. When applying decltype to the name of a (member) variable, we don't usually want some type that represents the properties of the variable when treated as an expression. Instead we want just the type the variable is declared with, without having to apply a ton of type traits to get at it. And that's exactly what decltype is specified to give us.

If we do care about the properties of the variable as an expression, then we can still get it rather easily, with an extra pair of parentheses.


It's not an oversight. It's interesting, that in Decltype and auto (revision 4) (N1705=04-0145) there is a statement:

The decltype rules now explicitly state that decltype((e)) == decltype(e)(as suggested by EWG).

But in Decltype (revision 6): proposed wording (N2115=06-018) one of the changes is

Parenthesized-expression inside decltype is not considered to be an id-expression.

There is no rationale in the wording, but I suppose this is kind of extension of decltype using a bit different syntax, in other words, it was intended to differentiate these cases.

The usage for that is shown in C++draft9.2.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

What is really interesting, is how it works with the return statement:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

My Visual Studio 2019 suggest me to remove redundant parenthesis, but actually they turn into decltype((i)) which changes return value to int& which makes it UB since returning reference to a local variable.