What is the rationale behind decltype behavior?

C++C++11C++14DecltypeType Deduction

C++ Problem Overview


As I understood in C++11 decltype(expression) is used to deduce the exact same type of the given expression. But when the expression is put into parentheses itself, then the deduces type is lvalue reference to the expression type. For example:

int x;
decltype(x) y = x;

is equivalent to int y = x; but,

int x;
decltype((x)) y = x;

is equivalent to int& y = x;.

Respectively

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

but

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

What is the rationale for this behavior to be chose by the standard committee?

Afterwords:

Now I observed that at least in the case of GCC 6.2 implementation when the expression in the parentheses is more complex for example decltype((x + x)) the deduced type is T, but not T&. This is even more confusing. I don't know whether this behavior is standard.

C++ Solutions


Solution 1 - C++

They wanted a way to get the type of declaration of an identifier.

They also wanted a way to get the type of an expression, including information about if it is a temporary or not.

decltype(x) gives the declared type of the identifier x. If you pass decltype something that is not an identifier, it determines the type, then appends & for lvalues, && for xvalues, and nothing for prvalues.

Conceptually you can think of it as the difference between the type of a variable and the type of an expression. But that is not quite how the standard describes it.

They could have used two different keywords to mean these two things. They did not.

Solution 2 - C++

There is some need for discriminating between an entity and an expression.

Consider the following question:

> How long is Mississippi?

There are two answers to this question:

  1. Mississippi is 2,320 miles long.
  2. Mississippi is 11 letters long.

Similarly when you ask about the type of x, and x is an identifier, it is not clear whether you mean the type that was used to declare that identifier (i.e. the type associated with the name x), or the type of the expression consisting of the sole mentioning of that identifier. In fact there could be two different keywords (e.g. entity_type and expr_type) instead of a single overloaded decltype. For some reason, the committee chose to overload decltype for those two different uses.

Solution 3 - C++

From one of the authors of the decltype proposal, J. Jarvi:

> It’s been a while, but here’s what I (think I) remember: > > Two separate keywords for differentiating these two kinds of semantics > was never considered. (Introducing new keywords is not done lightly). > > As to the change of the semantics of decltype((x)), discussion in the > core working group converged to treating (x) as an expression, rather > than an identifier, which perhaps is more “internally consistent” > with the language rules. > > People were aware that this could potentially be confusing in some > cases, but the consensus (while perhaps not everyone’s preference) was > eventually to be consistent with the standard’s prior definition of > what is an identifier and what is an expression. > > The example you link to [this question's example] is indeed surprising. At the time, deducing a > function’s return type from its return expression using decltype(auto) > was not yet part of the language, so I don’t think this particular use > case was on anyone’s radar.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbobeffView Question on Stackoverflow
Solution 1 - C++Yakk - Adam NevraumontView Answer on Stackoverflow
Solution 2 - C++LeonView Answer on Stackoverflow
Solution 3 - C++ColumboView Answer on Stackoverflow