From cppreference.com
A placeholder type specifier designates a placeholder type that will be replaced later, typically by deduction from an
.
Syntax
type-constraint(optional)auto(1) type-constraint(optional)decltype(auto)(2) (since C++14)type-constraint- (since C++20) a
name, optionally qualified, optionally followed by a template argument list enclosed in <>The placeholder auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. The placeholder decltype(auto) must be the sole constituent of the declared type.(since C++14)
If type-constraint is present, let T be the type deduced for the placeholder, the type-constraint introduces a
as follows:
If type-constraint is Concept<A1, ..., An>, then the constraint expression is Concept<T, A1, ..., An>;
otherwise (type-constraint is Concept without an argument list), the constraint expression is Concept<T>.
Deduction fails if the constraint expression is invalid or returns false.
(since C++20)Explanation
A placeholder type specifier may appear in the following contexts:
Parameter declarations
In the following parameter declarations, the type of the parameter declared can be of syntax (1):
If a parameter of a
has a placeholder type, the lambda expression is a generic lambda.
(since C++14)If a
has a placeholder type, its type is deduced from the corresponding template argument.
(since C++17)If a parameter of a
has a placeholder type, an
is declared.
(since C++20)Function declarations
A placeholder type can appear in the
for a
that includes a trailing return type.
autof()->int;// OK: f returns intautog(){return0.0;}// OK since C++14: g returns doubleautoh();// OK since C++14: h’s return type will be deduced when it is definedVariable declarations
The type of a variable declared using a placeholder type is deduced from its
. This use is allowed in an initializing declaration of a variable.
The placeholder type can only appear as one of the
in the declaration specifier sequence or as one of the type specifiers in a trailing return type that specifies the type that replaces such a declaration specifier. In this case, the declaration must declare at least one variable, and each variable must have a non-empty initializer.
// “auto”s in declaration specifiersautox=5;// OK: x has type intconstauto*v=&x,u=6;// OK: v has type const int*, u has type const intstaticautoy=0.0;// OK: y has type doubleautof()->int;auto(*fp)()->auto=f;// OK: the “auto” in the trailing return type// can be deduced from fStructured binding declarations
The auto specifier can be used in a
declaration.
(since C++17)new expressions
A placeholder type can be used in the type specifier sequence of the type-id of a
. In such a type-id, the placeholder type must appear as one of the type specifiers in the type specifier sequence or a trailing return type that specifies the type that replaces such a type specifier.
Function-style cast
The auto type specifier can be used as the type specifier of a
.
(since C++23)Notes
Until C++11, auto had the semantic of a
.
A program that uses a placeholder type in a context not explicitly stated above is ill-formed.
If a declaration declares multiple entities, and the declaration specifier sequence uses a placeholder type, the program is ill-formed if any of the following conditions is satisfied:
Some of the entities declared are not variables.
The type that replaces the placeholder type is not the same in each deduction.
autof()->int,i=0;// Error: declares a function and a variable with “auto”autoa=5,b={1,2};// Error: different types for “auto”The auto keyword may also be used in a nested name specifier. A nested name specifier of the form auto:: is a placeholder that is replaced by a class or enumeration type following the rules for
placeholder deduction.
(concepts TS)Feature-test macroValueStdFeature
(C++14)decltype(auto)Keywords
,
Example
Run this code
#include<iostream>#include<utility>template<classT,classU>autoadd(Tt,Uu){returnt+u;}// the return type is the type of operator+(T, U)// perfect forwarding of a function call must use decltype(auto)// in case the function it calls returns by referencetemplate<classF,class...Args>decltype(auto)PerfectForward(Ffun,Args&&...args){returnfun(std::forward<Args>(args)...);}template<auton>// C++17 auto parameter declarationautof()->std::pair<decltype(n),decltype(n)>// auto can't deduce from brace-init-list{return{n,n};}intmain(){autoa=1+2;// type of a is intautob=add(1,1.2);// type of b is doublestatic_assert(std::is_same_v<decltype(a),int>);static_assert(std::is_same_v<decltype(b),double>);autoc0=a;// type of c0 is int, holding a copy of adecltype(auto)c1=a;// type of c1 is int, holding a copy of adecltype(auto)c2=(a);// type of c2 is int&, an alias of astd::cout<<"before modification through c2, a = "<<a<<'\n';++c2;std::cout<<" after modification through c2, a = "<<a<<'\n';auto[v,w]=f<0>();//structured binding declarationautod={1,2};// OK: type of d is std::initializer_list<int>auton={5};// OK: type of n is std::initializer_list<int>// auto e{1, 2}; // Error as of DR n3922, std::initializer_list<int> beforeautom{5};// OK: type of m is int as of DR n3922, initializer_list<int> before// decltype(auto) z = { 1, 2 } // Error: {1, 2} is not an expression// auto is commonly used for unnamed types such as the types of lambda expressionsautolambda=[](intx){returnx+3;};// auto int x; // valid C++98, error as of C++11// auto x; // valid C, error in C++[](...){}(c0,c1,v,w,d,n,m,lambda);// suppresses "unused variable" warnings}Possible output:
before modification through c2, a = 3 after modification through c2, a = 4 Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR Applied to Behavior as published Correct behavior
C++11 the auto specifier could be used to declare a function with a trailing
return type and define a variable in one declaration statement prohibited
C++11 a parenthesized expression list could not be assigned to an auto variable allowed
C++11 a declaration with the auto specifier could define two variables
with types T and std::initializer_list<T> respectively prohibited
C++14 the auto specifier in decltype(auto) was also a placeholder not a placeholder
in this case
C++11 the return type of a function pointer type-id could be autoprohibited
C++11 the resolution of
prohibited the deduction
of the return type of function pointer variables from initializers allowed References
C++23 standard (ISO/IEC 14882:2024):
9.2.9.6 Placeholder type specifiers [dcl.spec.auto]
C++20 standard (ISO/IEC 14882:2020):
9.2.8.5 Placeholder type specifiers [dcl.spec.auto]
C++17 standard (ISO/IEC 14882:2017):
10.1.7.4 The auto specifier [dcl.spec.auto]
C++14 standard (ISO/IEC 14882:2014):
7.1.6.4 auto specifier [dcl.spec.auto]
C++11 standard (ISO/IEC 14882:2011):
7.1.6.4 auto specifier [dcl.spec.auto]