What is the rationale behind decltype behavior?

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.


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.


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.