template <class T> auto f(T t) { return t; } // #1
template auto f(int); // OK
template char f(char); // error, no matching template
template<> auto f(double); // OK, forward declaration with unknown return type
template <class T> T f(T t) { return t; } // OK, not functionally equivalent to #1
template char f(char); // OK, now there is a matching template
template auto f(float); // OK, matches #1
The auto and decltype(auto) type-specifiers designate a placeholder type that will be
replaced later, either by deduction from an initializer
or by explicit specification with signifies
that the type of a variable being declared shall be
deduced from its initializer or that a function declarator shall
include a trailing-return-type.
The auto type-specifier may
placeholder type can appear with a
function declarator in the decl-specifier-seq,
type-specifier-seq, conversion-function-id,
or trailing-return-type,
with a trailing-return-type
(8.3.5) in any context where such a declarator is valid. If the
function declarator includes a trailing-return-type (8.3.5), that specifies
the declared return type of the function.
If the declared return type of the function
contains a placeholder type, the return type of the function is deduced
from return statements in the body of the function, if any.
Otherwise, the type of the variable The type of a
variable declared using auto
or decltype(auto) is deduced from its
initializer. The name of the variable being declared shall not appear in
the initializer expression. This use of auto is allowed when declaring
variables in a block (6.3), in namespace scope (3.3.6), and in a
for-init-statement (6.5.3). auto or decltype(auto)
shall appear as one of the decl-specifiers
in the decl-specifier-seq and the decl-specifier-seq shall be followed by
one or more init-declarators, each of which shall have a non-empty
initializer.
[ Example:
auto x = 5; // OK: x has type int
const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int
static auto y = 0.0; // OK: y has type double
auto int r; // error: auto is not a storage-class-specifier
auto f() -> int; // OK: f returns int
auto g() { return 0.0; } // OK: g returns double
auto h(); // OK, h's return type will be deduced when it is defined
— end example ]
The auto type-specifier
A placeholder type can also be used in declaring a variable in the
condition of a selection statement (6.4) or an iteration statement (6.5),
in the type-specifier-seq in the new-type-id or type-id of a new-expression
(5.3.4), in a for-range-declaration, and in declaring a static data member
with a brace-or-equal-initializer that appears within the
member-specification of a class definition (9.4.2).
A program that uses auto or decltype(auto)
in a context not explicitly allowed in this section is ill-formed.
Once the type of a declarator-id has been
determined according to 8.3, the type of the declared variable using the
declarator-id When a variable declared
using a placeholder type is
initialized, or a return statement occurs in a function declared
with a return type that contains a placeholder type, the deduced return type or
variable type is determined from the type of its initializer.
In the case of a return with no operand, the initializer is considered to be void().
Let T be the declared type of the variable or return type of the function.
If the placeholder is the auto type-specifier,
the deduced type is determined using
the rules for template argument
deduction. Let T be the declared type that has been determined for a variable identifier d.
If the deduction is for a return statement and the
initializer is a braced-init-list (8.5.4), the program is ill-formed. Otherwise,
Obtain P from T by
replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer
is a braced-init-list (8.5.4), with std::initializer_list<U>.
The type deduced for the variable d is then the deduced A
determined Deduce a value for U using
the rules of template argument deduction from a function call (14.8.2.1),
where P is a function template parameter type and the initializer for d is the corresponding argument. If
the deduction fails, the declaration is ill-formed. Otherwise,
the type deduced for the variable or return type is obtained by
substituting the deduced U into P.
[ Example:
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
— end example ]
If the placeholder is the decltype(auto)
type-specifier, the declared type of the variable or return type of
the function shall be the placeholder alone. The type
deduced for the variable or return type is determined as
described in 7.1.6.2, as though the initializer had been the
operand of the decltype. [ Example:
int i;
int&& f();
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 ]
If the list of declarators contains more than one declarator, the type of each declared variable is determined
as described above. If a function with a declared return type that
contains a placeholder type has multiple return statements, the return type
is deduced for each return statement. In
either case, if the type deduced for the template parameter U is not
the same in each deduction, the
program is ill-formed.
[ Example:
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 a function with a declared return type that uses a placeholder type has
no return statements, the return type is deduced as though from
a return statement with no operand at the closing brace of the
function body. [ Example:
auto f() { } // OK, return type is void
auto* g() { } // error, cannot deduce auto* from void()
— end example ]
If the type of an entity with an undeduced placeholder
type is needed to determine the type of an expression, the program
is ill-formed. But once a return
statement has been seen in a function, the return type deduced from that
statement can be used in the rest of the function, including in other
return statements.
[ Example:
auto n = n; // error, n's type is unknown
auto f();
void g() { &f; } // error, f's return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum's return type is int
else
return sum(i-1)+i; // OK, sum's return type has been deduced
}
—end example]
Return type deduction for a function template with a placeholder
in its declared type occurs when the definition is instantiated even
if the function body
contains a return statement with a non-type-dependent operand.
[ Note: So any use of a specialization of the function template will cause
an implicit instantiation.
Any errors that arise from this instantiation are
not in the immediate context of the function type, and can result in the program
being ill-formed.
—end note ] [ Example:
template <class T> auto f(T t) { return t; } // return type deduced at instantiation time
typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type
template<class T> auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; } // instantiates both 'f's to determine return types, chooses second
—end example]
Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type
shall also use that placeholder, not
a deduced type. [ Example:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
decltype(auto) f(); // error, auto and decltype(auto) don't match
template <typename T> auto g(T t) { return t; } // #1
template auto g(int); // OK, return type is int
template char g(char); // error, no matching template
template<> auto g(double); // OK, forward declaration with unknown return type
template <class T> T g(T t) { return t; } // OK, not functionally equivalent to #1
template char g(char); // OK, now there is a matching template
template auto g(float); // still matches #1
void h() { return g(42); } // error, ambiguous
template <typename T> struct A {
friend T frf(T);
};
auto frf(int i) { return i; } // not a friend of A<int>
A function declared with a return type that uses a
placeholder type shall not be virtual (10.3).
An explicit instantiation declaration (14.7.2) does not
cause the instantiation of an entity declared using a placeholder type, but
it also does not prevent that entity from being instantiated as needed to
determine its type. [ Example:
template <typename T> auto f(T t) { return t; }
extern template auto f(int); // does not instantiate f<int>
int (*p)(int) = f; // instantiates f<int> to determine its return type,
// but an explicit instantiation definition is still required somewhere in the program
—end example ]
Change 5.1.2¶4: