[dcl.spec] (original) (raw)

9 Declarations [dcl.dcl]

9.2 Specifiers [dcl.spec]

9.2.1 General [dcl.spec.general]

The specifiers that can be used in a declaration are

decl-specifier:
storage-class-specifier
defining-type-specifier
function-specifier
friend
typedef
constexpr
consteval
constinit
inline

The attribute-specifier-seqaffects the type only for the declaration it appears in, not other declarations involving the same type.

Each decl-specifiershall appear at most once in a complete decl-specifier-seq, except that long may appear twice.

At most one of the constexpr, consteval, and constinit keywords shall appear in a decl-specifier-seq.

If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous defining-type-specifier other than a cv-qualifier in thedecl-specifier-seq.

The sequence shall be self-consistent as described below.

[Example 1: typedef char* Pc;static Pc;

Here, the declaration static Pc is ill-formed because no name was specified for the static variable of type Pc.

To get a variable called Pc, a type-specifier (other thanconst or volatile) has to be present to indicate that the typedef-name Pc is the name being (re)declared, rather than being part of the decl-specifier sequence.

For another example,void f(const Pc); void g(const int Pc);

— _end example_]

[Note 1:

Since signed, unsigned, long, and shortby default imply int, a type-name appearing after one of those specifiers is treated as the name being (re)declared.

[Example 2: void h(unsigned Pc); void k(unsigned int Pc); — _end example_]

— _end note_]

9.2.2 Storage class specifiers [dcl.stc]

The storage class specifiers are

storage-class-specifier:
static
thread_­local
extern
mutable

At most one storage-class-specifier shall appear in a givendecl-specifier-seq, except that thread_­local may appear with static orextern.

If thread_­local appears in any declaration of a variable it shall be present in all declarations of that entity.

If astorage-class-specifierappears in a decl-specifier-seq, there can be notypedef specifier in the same decl-specifier-seq and the init-declarator-list or member-declarator-listof the declaration shall not be empty (except for an anonymous union declared in a named namespace or in the global namespace, which shall be declaredstatic ([class.union.anon])).

Thestorage-class-specifier applies to the name declared by eachinit-declarator in the list and not to any names declared by other specifiers.

[Note 2:

A variable declared without a storage-class-specifierat block scope or declared as a function parameter has automatic storage duration by default.

— _end note_]

It shall be applied only to the declaration of a variable of namespace or block scope, to a structured binding declaration ([dcl.struct.bind]), or to the declaration of a static data member.

When thread_­local is applied to a variable of block scope thestorage-class-specifier static is implied if no otherstorage-class-specifier appears in thedecl-specifier-seq.

The static specifier shall be applied only to the declaration of a variable or function, to a structured binding declaration ([dcl.struct.bind]), or to the declaration of an anonymous union ([class.union.anon]).

There can be nostatic function declarations within a block, nor anystatic function parameters.

A static specifier used in the declaration of a variable declares the variable to havestatic storage duration, unless accompanied by thethread_­local specifier, which declares the variable to havethread storage duration.

A static specifier can be used in declarations of class members; [class.static] describes its effect.

For the linkage of a name declared with a static specifier, see [basic.link].

The extern specifier shall be applied only to the declaration of a variable or function.

The extern specifier shall not be used in the declaration of a class member or function parameter.

For the linkage of a name declared with an extern specifier, see [basic.link].

The linkages implied by successive declarations for a given entity shall agree.

That is, within a given scope, each declaration declaring the same variable name or the same overloading of a function name shall imply the same linkage.

[Example 1: static char* f(); char* f() { } char* g(); static char* g() { } void h();inline void h(); inline void l();void l(); inline void m();extern void m(); static void n();inline void n(); static int a; int a; static int b; extern int b; int c; static int c; extern int d; static int d; — _end example_]

The name of a declared but undefined class can be used in anextern declaration.

Such a declaration can only be used in ways that do not require a complete class type.

[Example 2: struct S;extern S a;extern S f();extern void g(S);void h() { g(a); f(); } — _end example_]

The mutable specifier shall appear only in the declaration of a non-static data memberwhose type is neither const-qualified nor a reference type.

[Example 3: class X { mutable const int* p; mutable int* const q; }; — _end example_]

[Note 4:

The mutable specifier on a class data member nullifies aconst specifier applied to the containing class object and permits modification of the mutable class member even though the rest of the object is const ([basic.type.qualifier], [dcl.type.cv]).

— _end note_]

9.2.3 Function specifiers [dcl.fct.spec]

Afunction-specifiercan be used only in a function declaration.

The virtual specifier shall be used only in the initial declaration of a non-static member function; see [class.virtual].

If the constant expression evaluates to true, the function is explicit.

Otherwise, the function is not explicit.

A ( token that follows explicit is parsed as part of the explicit-specifier.

9.2.4 The typedef specifier [dcl.typedef]

Declarations containing the decl-specifier typedefdeclare identifiers that can be used later for namingfundamental or compoundtypes.

If a typedef specifier appears in a declaration without a declarator, the program is ill-formed.

A name declared with the typedef specifier becomes atypedef-name.

Atypedef-name does not introduce a new type the way a class declaration ([class.name]) or enum declaration ([dcl.enum]) does.

[Example 1:

Aftertypedef int MILES, *KLICKSP;the constructionsMILES distance;extern KLICKSP metricp;are all correct declarations; the type of distance isint and that of metricp is “pointer to int”.

— _end example_]

Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.

In particular, it does not define a new type.

[Example 2: using handler_t = void (*)(int);extern handler_t ignore;extern void (*ignore)(int); using cell = pair<void*, cell*>; — _end example_]

In a given non-class scope, a typedef specifier can be used to redeclare the name of any type declared in that scope to refer to the type to which it already refers.

[Example 3: typedef struct s { } s;typedef int I;typedef int I;typedef I I; — _end example_]

In a given class scope, a typedef specifier can be used to redeclare any class-name declared in that scope that is not also a typedef-name to refer to the type to which it already refers.

[Example 4: struct S { typedef struct A { } A; typedef struct B B; typedef A A; }; — _end example_]

If a typedef specifier is used to redeclare in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by anelaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively.

[Example 5: struct S;typedef struct S S;int main() { struct S* p; } struct S { }; — _end example_]

In a given scope, a typedef specifier shall not be used to redeclare the name of any type declared in that scope to refer to a different type.

[Example 6: class complex { };typedef int complex; — _end example_]

Similarly, in a given scope, a class or enumeration shall not be declared with the same name as a typedef-name that is declared in that scope and refers to a type other than the class or enumeration itself.

[Example 7: typedef int complex;class complex { }; — _end example_]

[Example 8: struct S { S();~S();};typedef struct S T; S a = T(); struct T * p; — _end example_]

If the typedef declaration defines an unnamed class or enumeration, the firsttypedef-name declared by the declaration to be that type is used to denote the type for linkage purposes only ([basic.link]).

[Note 2:

A typedef declaration involving a lambda-expressiondoes not itself define the associated closure type, and so the closure type is not given a name for linkage purposes.

— _end note_]

[Example 9: typedef struct { } *ps, S; typedef decltype([]{}) C; — _end example_]

An unnamed class with a typedef name for linkage purposes shall not

and all member classes shall also satisfy these requirements (recursively).

[Example 10: typedef struct { int f() {} } X; — _end example_]

9.2.6 The constexpr and consteval specifiers [dcl.constexpr]

The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template.

The consteval specifier shall be applied only to the declaration of a function or function template.

A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable.

If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.

[Note 1:

An explicit specialization can differ from the template declaration with respect to the constexpr or consteval specifier.

— _end note_]

[Note 2:

Function parameters cannot be declared constexpr.

— _end note_]

[Example 1: constexpr void square(int &x); constexpr int bufsz = 1024; constexpr struct pixel { int x;int y;constexpr pixel(int); };constexpr pixel::pixel(int a) : x(a), y(x) { square(x); } constexpr pixel small(2); constexpr void square(int &x) { x *= x;} constexpr pixel large(4); int next(constexpr int x) { return x + 1;} extern constexpr int memsz; — _end example_]

A constexpr or consteval specifier used in the declaration of a function declares that function to be a constexpr function.

A function or constructor declared with the consteval specifier is called an immediate function.

A destructor, an allocation function, or a deallocation function shall not be declared with the consteval specifier.

The definition of a constexpr function shall satisfy the following requirements:

[Example 2: constexpr int square(int x) { return x * x; } constexpr long long_max() { return 2147483647; } constexpr int abs(int x) { if (x < 0) x = -x;return x; } constexpr int first(int n) { static int value = n; return value;} constexpr int uninit() { struct { int a; } s;return s.a; } constexpr int prev(int x) { return --x; } constexpr int g(int x, int n) { int r = 1;while (--n > 0) r *= x;return r;} — _end example_]

The definition of a constexpr constructor whose function-body is not = deleteshall additionally satisfy the following requirements:

[Example 3: struct Length { constexpr explicit Length(int i = 0) : val(i) { } private: int val;}; — _end example_]

The definition of a constexpr destructor whose function-body is not = deleteshall additionally satisfy the following requirement:

For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of acore constant expression, or, for a constructor, an evaluated subexpression of the initialization full-expression of some constant-initialized object ([basic.start.static]), the program is ill-formed, no diagnostic required.

[Example 4: constexpr int f(bool b) { return b ? throw 0 : 0; } constexpr int f() { return f(true); } struct B { constexpr B(int x) : i(0) { } int i;};int global;struct D : B { constexpr D() : B(global) { } }; — _end example_]

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function, that specialization is still a constexpr function, even though a call to such a function cannot appear in a constant expression.

If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.

An invocation of a constexpr function in a given context produces the same result as an invocation of an equivalent non-constexpr function in the same context in all respects except that

[Note 4:

Declaring a function constexpr can change whether an expression is a constant expression.

This can indirectly cause calls to std​::​is_­constant_­evaluatedwithin an invocation of the function to produce a different value.

— _end note_]

The constexpr and consteval specifiers have no effect on the type of a constexpr function.

[Example 5: constexpr int bar(int x, int y) { return x + y + x*y; } int bar(int x, int y) { return x * 2 + 3 * y; } — _end example_]

A constexpr specifier used in an object declaration declares the object as const.

Such an object shall have literal type and shall be initialized.

In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

A constexpr variable shall have constant destruction.

[Example 6: struct pixel { int x, y;};constexpr pixel ur = { 1294, 1024 }; constexpr pixel origin; — _end example_]

9.2.7 The constinit specifier [dcl.constinit]

The constinit specifier shall be applied only to a declaration of a variable with static or thread storage duration.

If the specifier is applied to any declaration of a variable, it shall be applied to the initializing declaration.

No diagnostic is required if no constinit declaration is reachable at the point of the initializing declaration.

If a variable declared with the constinit specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.

[Note 1:

The constinit specifier ensures that the variable is initialized during static initialization ([basic.start.static]).

— _end note_]

[Example 1: const char * g() { return "dynamic initialization"; } constexpr const char * f(bool p) { return p ? "constant initializer" : g(); } constinit const char * c = f(true); constinit const char * d = f(false); — _end example_]

9.2.8 The inline specifier [dcl.inline]

The inline specifier shall be applied only to the declaration of a variable or function.

The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism.

An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions specified in this subclause shall still be respected.

[Note 1:

The inline keyword has no effect on the linkage of a function.

In certain cases, an inline function cannot use names with internal linkage; see [basic.link].

— _end note_]

A variable declaration with an inline specifier declares aninline variable.

The inline specifier shall not appear on a block scope declaration or on the declaration of a function parameter.

If the inline specifier is used in a friend function declaration, that declaration shall be a definition or the function shall have previously been declared inline.

If a definition of a function or variable is reachable at the point of its first declaration as inline, the program is ill-formed.

If a function or variable with external or module linkage is declared inline in one definition domain, an inline declaration of it shall be reachable from the end of every definition domain in which it is declared; no diagnostic is required.

[Note 2:

A call to an inline function or a use of an inline variable can be encountered before its definition becomes reachable in a translation unit.

— _end note_]

[Note 3:

An inline function or variable with external or module linkage has the same address in all translation units.

A static local variable in an inline function with external or module linkage always refers to the same object.

A type defined within the body of an inline function with external or module linkage is the same type in every translation unit.

— _end note_]

If an inline function or variable that is attached to a named module is declared in a definition domain, it shall be defined in that domain.

[Note 4:

A constexpr function is implicitly inline.

In the global module, a function defined within a class definition is implicitly inline ([class.mfct], [class.friend]).

— _end note_]

9.2.9 Type specifiers [dcl.type]

9.2.9.1 General [dcl.type.general]

Theattribute-specifier-seq affects the type only for the declaration it appears in, not other declarations involving the same type.

As a general rule, at most onedefining-type-specifieris allowed in the completedecl-specifier-seq of a declaration or in adefining-type-specifier-seq, and at most onetype-specifieris allowed in atype-specifier-seq.

The only exceptions to this rule are the following:

Except in a declaration of a constructor, destructor, or conversion function, at least one defining-type-specifier that is not acv-qualifier shall appear in a completetype-specifier-seq or a completedecl-specifier-seq.90

[Note 1:

The remainingtype-specifiers are discussed in the rest of [dcl.type].

— _end note_]

9.2.9.2 The cv-qualifier s [dcl.type.cv]

Redundant cv-qualifications are ignored.

[Note 2:

For example, these could be introduced by typedefs.

— _end note_]

[Note 3:

Declaring a variable const can affect its linkage ([dcl.stc]) and its usability in constant expressions.

As described in [dcl.init], the definition of an object or subobject of const-qualified type must specify an initializer or be subject to default-initialization.

— _end note_]

A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path.

[Note 4:

Cv-qualifiers are supported by the type system so that they cannot be subverted without casting.

— _end note_]

[Example 1: const int ci = 3; ci = 4; int i = 2; const int* cip; cip = &i; *cip = 4; int* ip; ip = const_cast<int*>(cip); *ip = 4; const int* ciq = new const int (3); int* iq = const_cast<int*>(ciq); *iq = 4;

For another example,struct X { mutable int i;int j;};struct Y { X x; Y();};const Y y; y.x.i++; y.x.j++; Y* p = const_cast<Y*>(&y); p->x.i = 99; p->x.j = 99;

— _end example_]

The semantics of an access through a volatile glvalue areimplementation-defined.

If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.

[Note 5:

volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation.

Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object.

In general, the semantics of volatile are intended to be the same in C++ as they are in C.

— _end note_]

9.2.9.3 Simple type specifiers [dcl.type.simple]

The simple type specifiers are

A placeholder-type-specifieris a placeholder for a type to be deduced ([dcl.spec.auto]).

A deducible template is either a class template or is an alias template whose defining-type-id is of the form

[Note 1:

An injected-class-name is never interpreted as a template-namein contexts where class template argument deduction would be performed ([temp.local]).

— _end note_]

The othersimple-type-specifier_s_specify either a previously-declared type, a type determined from an expression, or one of the fundamental types ([basic.fundamental]).

Table 14 summarizes the valid combinations ofsimple-type-specifier_s_and the types they specify.

Table 14: simple-type-specifiers and the types they specify [tab:dcl.type.simple]

🔗 Specifier(s) Type
🔗 type-name the type named
🔗 simple-template-id the type as defined in [temp.names]
🔗 decltype-specifier the type as defined in [dcl.type.decltype]
🔗 placeholder-type-specifier the type as defined in [dcl.spec.auto]
🔗 template-name the type as defined in [dcl.type.class.deduct]
🔗 char “char”
🔗 unsigned char “unsigned char”
🔗 signed char “signed char”
🔗 char8_­t “char8_­t”
🔗 char16_­t “char16_­t”
🔗 char32_­t “char32_­t”
🔗 bool “bool”
🔗 unsigned “unsigned int”
🔗 unsigned int “unsigned int”
🔗 signed “int”
🔗 signed int “int”
🔗 int “int”
🔗 unsigned short int “unsigned short int”
🔗 unsigned short “unsigned short int”
🔗 unsigned long int “unsigned long int”
🔗 unsigned long “unsigned long int”
🔗 unsigned long long int “unsigned long long int”
🔗 unsigned long long “unsigned long long int”
🔗 signed long int “long int”
🔗 signed long “long int”
🔗 signed long long int “long long int”
🔗 signed long long “long long int”
🔗 long long int “long long int”
🔗 long long “long long int”
🔗 long int “long int”
🔗 long “long int”
🔗 signed short int “short int”
🔗 signed short “short int”
🔗 short int “short int”
🔗 short “short int”
🔗 wchar_­t “wchar_­t”
🔗 float “float”
🔗 double “double”
🔗 long double “long double”
🔗 void “void”

When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order.

[Note 2:

It is implementation-defined whether objects of char type are represented as signed or unsigned quantities.

The signed specifier forces char objects to be signed; it is redundant in other contexts.

— _end note_]

9.2.9.4 Elaborated type specifiers [dcl.type.elab]

An attribute-specifier-seq shall not appear in an elaborated-type-specifierunless the latter is the sole constituent of a declaration.

If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization ([temp.expl.spec]), an explicit instantiation ([temp.explicit]) or it has one of the following forms:

[Note 2:

This implies that, within a class template with a templatetype-parameter T, the declarationfriend class T;is ill-formed.

However, the similar declaration friend T; is allowed ([class.friend]).

— _end note_]

The class-key or enum keyword present in theelaborated-type-specifier shall agree in kind with the declaration to which the name in theelaborated-type-specifier refers.

This rule also applies to the form of elaborated-type-specifier that declares aclass-name or friend class since it can be construed as referring to the definition of the class.

Thus, in anyelaborated-type-specifier, the enum keyword shall be used to refer to an enumeration ([dcl.enum]), the union class-key shall be used to refer to a union ([class.union]), and either the class or struct class-key shall be used to refer to a non-union class ([class.pre]).

[Example 1: enum class E { a, b };enum E x = E::a; struct S { } s;class S* p = &s; — _end example_]

9.2.9.5 Decltype specifiers [dcl.type.decltype]

For an expression E, the type denoted by decltype(E) is defined as follows:

[Example 1: const int&& foo();int i;struct A { double x; };const A* a = new A();decltype(foo()) x1 = 17; decltype(i) x2; decltype(a->x) x3; decltype((a->x)) x4 = x3; — _end example_]

[Note 1:

The rules for determining types involving decltype(auto) are specified in [dcl.spec.auto].

— _end note_]

If the operand of a decltype-specifier is a prvalue and is not a (possibly parenthesized) immediate invocation ([expr.const]), the temporary materialization conversion is not applied ([conv.rval]) and no result object is provided for the prvalue.

The type of the prvalue may be incomplete or an abstract class type.

[Note 2:

As a result, storage is not allocated for the prvalue and it is not destroyed.

Thus, a class type is not instantiated as a result of being the type of a function call in this context.

In this context, the common purpose of writing the expression is merely to refer to its type.

In that sense, adecltype-specifier is analogous to a use of a typedef-name, so the usual reasons for requiring a complete type do not apply.

In particular, it is not necessary to allocate storage for a temporary object or to enforce the semantic constraints associated with invoking the type's destructor.

— _end note_]

[Note 3:

Unlike the preceding rule, parentheses have no special meaning in this context.

— _end note_]

[Example 2: template<class T> struct A { ~A() = delete; };template<class T> auto h() -> A<T>;template<class T> auto i(T) -> T;template<class T> auto f(T) -> decltype(i(h<T>())); template<class T> auto f(T) -> void;auto g() -> void { f(42); } template<class T> auto q(T) -> decltype((h<T>())); void r() { q(42); } — _end example_]

9.2.9.6 Placeholder type specifiers [dcl.spec.auto]

9.2.9.6.1 General [dcl.spec.auto.general]

A placeholder-type-specifierdesignates a placeholder type that will be replaced later by deduction from an initializer.

[Note 1:

Having a generic parameter type placeholder signifies that the function is an abbreviated function template ([dcl.fct]) or the lambda is a generic lambda ([expr.prim.lambda]).

— _end note_]

Otherwise, the function declarator shall declare a function.

If the declared return type of the function contains a placeholder type, the return type of the function is deduced from non-discarded return statements, if any, in the body of the function ([stmt.if]).

The type of a variable declared using a placeholder type is deduced from its initializer.

This use is allowed in an initializing declaration ([dcl.init]) of a variable.

The placeholder type shall appear as one of thedecl-specifiers in thedecl-specifier-seq and thedecl-specifier-seqshall be followed by one or moredeclarators, each of which shall be followed by a non-emptyinitializer.

[Example 1: auto x = 5; const auto *v = &x, u = 6; static auto y = 0.0; auto int r; auto f() -> int; auto g() { return 0.0; } auto h(); — _end example_]

The auto type-specifiercan also be used to introduce a structured binding declaration ([dcl.struct.bind]).

A program that uses a placeholder type in a context not explicitly allowed in [dcl.spec.auto] is ill-formed.

The type of each declared variable is determined by placeholder type deduction, and if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed.

[Example 2: auto x = 5, *y = &x; auto a = 5, b = { 1, 2 }; — _end example_]

If a function with a declared return type that contains a placeholder type has multiple non-discarded return statements, the return type is deduced for each such return statement.

If the type deduced is not the same in each deduction, the program is ill-formed.

If a function with a declared return type that uses a placeholder type has no non-discarded return statements, the return type is deduced as though from areturn statement with no operand at the closing brace of the function body.

[Example 3: auto f() { } auto* g() { } — _end example_]

An exported function with a declared return type that uses a placeholder type shall be defined in the translation unit containing its exported declaration, outside the private-module-fragment (if any).

[Note 2:

The deduced return type cannot have a name with internal linkage ([basic.link]).

— _end note_]

If the name of an entity with an undeduced placeholder type appears in an expression, the program is ill-formed.

Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in otherreturn statements.

[Example 4: auto n = n; auto f();void g() { &f; } auto sum(int i) { if (i == 1) return i; else return sum(i-1)+i; } — _end example_]

Return type deduction for a templated entity that is a function or 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 3:

Therefore, 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 ([temp.deduct]).

— _end note_]

[Example 5: template <class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } — _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.

Similarly, redeclarations or specializations of a function or function template with a declared return type that does not use a placeholder type shall not use a placeholder.

[Example 6: auto f();auto f() { return 42; } auto f(); int f(); decltype(auto) f(); template <typename T> auto g(T t) { return t; } template auto g(int); template char g(char); template<> auto g(double); template <class T> T g(T t) { return t; } template char g(char); template auto g(float); void h() { return g(42); } template <typename T> struct A { friend T frf(T);};auto frf(int i) { return i; } extern int v;auto v = 17; struct S { static int i;};auto S::i = 23; — _end example_]

A function declared with a return type that uses a placeholder type shall not be virtual.

A function declared with a return type that uses a placeholder type shall not be a coroutine ([dcl.fct.def.coroutine]).

An explicit instantiation declaration 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 7: template <typename T> auto f(T t) { return t; } extern template auto f(int); int (*p)(int) = f; — _end example_]

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

Placeholder type deductionis 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:

In the case of a return statement with no operand or with an operand of type void,T shall be eithertype-constraint decltype(auto) orcv type-constraint auto.

If the placeholder-type-specifier is of the formtype-constraint auto, the deduced type replacing Tis determined using the rules for template argument deduction.

Obtain P fromT by replacing the occurrences oftype-constraint auto either with a new invented type template parameter U or, if the initialization is copy-list-initialization, withstd​::​initializer_­list<U>.

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 }; auto x2 = { 1, 2.0 }; auto x3{ 1, 2 }; auto x4 = { 3 }; auto x5{ 3 }; — _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 formtype-constraint decltype(auto),T shall be the placeholder alone.

The type deduced for T is determined as described in [dcl.type.decltype], as thoughE had been the operand of the decltype.

[Example 3: int i;int&& f();auto x2a(i); decltype(auto) x2d(i); auto x3a = i; decltype(auto) x3d = i; auto x4a = (i); decltype(auto) x4d = (i); auto x5a = f(); decltype(auto) x5d = f(); auto x6a = { 1, 2 }; decltype(auto) x6d = { 1, 2 }; auto *x7a = &i; decltype(auto)*x7d = &i; — _end example_]

9.2.9.7 Deduced class template specialization types [dcl.type.class.deduct]

If a placeholder for a deduced class type appears as a decl-specifierin the decl-specifier-seqof an initializing declaration ([dcl.init]) of a variable, the declared type of the variable shall be cv T, where T is the placeholder.

[Example 1: template <class ...T> struct A { A(T...) {} }; A x[29]{}; const A& y{}; — _end example_]

The placeholder is replaced by the return type of the function selected by overload resolution for class template deduction ([over.match.class.deduct]).

A placeholder for a deduced class type shall not appear in any other context.

[Example 2: template<class T> struct container { container(T t) {} template<class Iter> container(Iter beg, Iter end);};template<class Iter>container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>; std::vector<double> v = { }; container c(7); auto d = container(v.begin(), v.end()); container e{5, 6}; — _end example_]