9 Declarations [dcl.dcl]

9.2 Specifiers [dcl.spec]

9.2.9 Type specifiers [dcl.type]

9.2.9.6 Placeholder type specifiers [dcl.spec.auto]

9.2.9.6.2 Placeholder type deduction [dcl.type.auto.deduct]

Placeholder type deduction is the process by which a type containing a placeholder type is replaced by a deduced type.
A type T containing a placeholder type, and a corresponding initializer E, are determined as follows:
  • for a non-discarded return statement that occurs in a function declared with a return type that contains a placeholder type, T is the declared return type and E is the operand of the return statement.
    If the return statement has no operand, then E is void();
  • for a variable declared with a type that contains a placeholder type, T is the declared type of the variable and E is the initializer.
    If the initialization is direct-list-initialization, the initializer shall be a braced-init-list containing only a single assignment-expression and E is the assignment-expression;
  • for a non-type template parameter declared with a type that contains a placeholder type, T is the declared type of the non-type template parameter and E is the corresponding template argument.
In the case of a return statement with no operand or with an operand of type void, T shall be either type-constraint decltype(auto) or cv type-constraint auto.
If the deduction is for a return statement and E is a braced-init-list ([dcl.init.list]), the program is ill-formed.
If the placeholder-type-specifier is of the form type-constraint auto, the deduced type replacing T is determined using the rules for template argument deduction.
Obtain P from T by replacing the occurrences of type-constraint auto either with a new invented type template parameter U or, if the initialization is copy-list-initialization, with std​::​initializer_­list<U>.
Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is E.
If the deduction fails, the declaration is ill-formed.
Otherwise, is obtained by substituting the deduced U into P.
[Example 1: auto x1 = { 1, 2 }; // decltype(x1) is std​::​initializer_­list<int> auto x2 = { 1, 2.0 }; // error: cannot deduce element type auto x3{ 1, 2 }; // error: not a single element auto x4 = { 3 }; // decltype(x4) is std​::​initializer_­list<int> auto x5{ 3 }; // decltype(x5) is int — end example]
[Example 2: const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template: template <class U> void f(const U& u);
— end example]
If the placeholder-type-specifier is of the form type-constraint decltype(auto), T shall be the placeholder alone.
The type deduced for T is determined as described in [dcl.type.decltype], as though E had been the operand of the decltype.
[Example 3: int i; int&& f(); auto x2a(i); // decltype(x2a) is int decltype(auto) x2d(i); // decltype(x2d) is int auto x3a = i; // decltype(x3a) is int decltype(auto) x3d = i; // decltype(x3d) is int auto x4a = (i); // decltype(x4a) is int decltype(auto) x4d = (i); // decltype(x4d) is int& auto x5a = f(); // decltype(x5a) is int decltype(auto) x5d = f(); // decltype(x5d) is int&& auto x6a = { 1, 2 }; // decltype(x6a) is std​::​initializer_­list<int> decltype(auto) x6d = { 1, 2 }; // error: { 1, 2 } is not an expression auto *x7a = &i; // decltype(x7a) is int* decltype(auto)*x7d = &i; // error: declared type is not plain decltype(auto) — end example]
For a placeholder-type-specifier with a type-constraint, the immediately-declared constraint ([temp.param]) of the type-constraint for the type deduced for the placeholder shall be satisfied.