R Sahu
R Sahu

Reputation: 206717

Why is the behavior of decltype defined the way it is?

From C++ Draft Standard N3337:

7.1.6.2 Simple type specifiers

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;

If I understand the above correctly,

int a;
decltype(a) b = 10;    // type of b is int

int* ap;
decltype(*ap) c = 10;  // Does not work since type of c is int&

Can you explain, or point to some documentation that explains, why decltype(*ap) couldn't have been just int?

Upvotes: 5

Views: 142

Answers (1)

Howard Hinnant
Howard Hinnant

Reputation: 219345

The standardization effort of decltype was a herculean effort spanning many years. There were 7 versions of this paper before the committee finally accepted it. The versions were:

Remarkably, the seeds of the behavior which you question are in the very first revision: N1478, which introduces the need for "two types of typeof: either to preserve or to drop references in types."

This paper goes on to give a rationale for the reference-preserving variant, including this quote:

On the other hand, the reference-dropping semantics fails to provide a mechanism for exactly expressing the return types of generic functions, as demonstrated by Strous- trup [Str02]. This implies that a reference-dropping typeof would cause problems for writers of generic libraries.

There is no substitute for reading through these papers. However one might summarize that decltype serves two purposes:

  1. To report the declared type of an identifier.
  2. To report the type of an expression.

For the second use case, recall that expressions are never reference types, but are instead one of lvalues, xvalues, or prvalues. By convention, when decltype is reporting the type of an lvalue expression, it makes the type an lvalue reference, and when the expression is an xvalue, the reported type becomes an rvalue reference.

In your example, *ap is an expression, whereas a is an identifier. So your example makes use of both use cases, as first introduced in N1478.

It is also instructive to note that decltype was not designed in isolation. The rest of the C++ language was evolving during this time period (e.g. rvalue references), and the design of decltype was iterated to keep pace.

Also note that once the decltype proposal was accepted, it continued (and continues to this day) to evolve. See this list of issues:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_index.html

specifically section 7.1.6.2 (which is the section where the bulk of the decltype specification lives).

Upvotes: 6

Related Questions