9 Declarations [dcl.dcl]

9.1 Preamble [dcl.pre]

Declarations generally specify how names are to be interpreted.
[Note 1:  — end note]
Certain declarations contain one or more scopes ([basic.scope.scope]).
Unless otherwise stated, utterances in [dcl.dcl] about components in, of, or contained by a declaration or subcomponent thereof refer only to those components of the declaration that are not nested within scopes nested within the declaration.
If a name-declaration matches the syntactic requirements of friend-type-declaration, it is a friend-type-declaration.
Attributes are described in [dcl.attr].
decl-specifiers, the principal components of a decl-specifier-seq, are described in [dcl.spec].
declarators, the components of an init-declarator-list, are described in [dcl.decl].
The attribute-specifier-seq appertains to each of the entities declared by the declarators of the init-declarator-list.
[Note 2: 
In the declaration for an entity, attributes appertaining to that entity can appear at the start of the declaration and after the declarator-id for that declaration.
— end note]
[Example 1: [[noreturn]] void f [[noreturn]] (); // OK — end example]
If a declarator-id is a name, the init-declarator and (hence) the declaration introduce that name.
[Note 3: 
Otherwise, the declarator-id is a qualified-id or names a destructor or its unqualified-id is a template-id and no name is introduced.
— end note]
The defining-type-specifiers ([dcl.type]) in the decl-specifier-seq and the recursive declarator structure describe a type ([dcl.meaning]), which is then associated with the declarator-id.
In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class ([class.pre]) or enumeration ([dcl.enum]), that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key ([class.name]), or an enum-specifier.
In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are also declared (as class-names, enum-names, or enumerators, depending on the syntax).
In such cases, the decl-specifier-seq shall (re)introduce one or more names into the program.
[Example 2: enum { }; // error typedef class { }; // error — end example]
Each decl-specifier in the decl-specifier-seq shall be static, thread_local, auto ([dcl.spec.auto]), or a cv-qualifier.
[Example 3: template<class T> concept C = true; C auto [x, y] = std::pair{1, 2}; // error: constrained placeholder-type-specifier // not permitted for structured bindings — end example]
The initializer shall be of the form “= assignment-expression”, of the form “{ assignment-expression }”, or of the form “( assignment-expression ).
If the structured-binding-declaration appears as a condition, the assignment-expression shall be of non-union class type.
Otherwise, the assignment-expression shall be of array or non-union class type.
If the decl-specifier-seq contains the typedef specifier, the declaration is a typedef declaration and each declarator-id is declared to be a typedef-name, synonymous with its associated type ([dcl.typedef]).
[Note 4:  — end note]
Otherwise, if the type associated with a declarator-id is a function type ([dcl.fct]), the declaration is a function declaration.
Otherwise, if the type associated with a declarator-id is an object or reference type, the declaration is an object declaration.
Otherwise, the program is ill-formed.
[Example 4: int f(), x; // OK, function declaration for f and object declaration for x extern void g(), // OK, function declaration for g y; // error: void is not an object type — end example]
An object definition causes storage of appropriate size and alignment to be reserved and any appropriate initialization ([dcl.init]) to be done.
Syntactic components beyond those found in the general form of simple-declaration are added to a function declaration to make a function-definition.
A token sequence starting with { or = is treated as a function-body ([dcl.fct.def.general]) if the type of the declarator-id ([dcl.meaning.general]) is a function type, and is otherwise treated as a brace-or-equal-initializer ([dcl.init.general]).
[Note 5: 
If the declaration acquires a function type through template instantiation, the program is ill-formed; see [temp.spec.general].
The function type of a function definition cannot be specified with a typedef-name ([dcl.fct]).
— end note]
A nodeclspec-function-declaration shall declare a constructor, destructor, or conversion function.
[Note 6: 
Because a member function cannot be subject to a non-defining declaration outside of a class definition ([class.mfct]), a nodeclspec-function-declaration can only be used in a template-declaration ([temp.pre]), explicit-instantiation ([temp.explicit]), or explicit-specialization ([temp.expl.spec]).
— end note]
If a static_assert-message matches the syntactic requirements of unevaluated-string, it is an unevaluated-string and the text of the static_assert-message is the text of the unevaluated-string.
Otherwise, a static_assert-message shall be an expression M such that
  • the expression M.size() is implicitly convertible to the type std​::​size_t, and
  • the expression M.data() is implicitly convertible to the type “pointer to const char.
In a static_assert-declaration, the constant-expression E is contextually converted to bool and the converted expression shall be a constant expression ([expr.const]).
If the value of the expression E when so converted is true or the expression is evaluated in the context of a template definition, the declaration has no effect and the static_assert-message is an unevaluated operand ([expr.context]).
Otherwise, the static_assert-declaration fails and
  • the program is ill-formed, and
  • if the static_assert-message is a constant-expression M,
    • M.size() shall be a converted constant expression of type std​::​size_t and let N denote the value of that expression,
    • M.data(), implicitly converted to the type “pointer to const char”, shall be a core constant expression and let D denote the converted expression,
    • for each i where , D[i] shall be an integral constant expression, and
    • the text of the static_assert-message is formed by the sequence of N code units, starting at D, of the ordinary literal encoding ([lex.charset]).
Recommended practice: When a static_assert-declaration fails, the resulting diagnostic message should include the text of the static_assert-message, if one is supplied.
[Example 5: static_assert(sizeof(int) == sizeof(void*), "wrong pointer size"); static_assert(sizeof(int[2])); // OK, narrowing allowed template <class T> void f(T t) { if constexpr (sizeof(T) == sizeof(int)) { use(t); } else { static_assert(false, "must be int-sized"); } } void g(char c) { f(0); // OK f(c); // error on implementations where sizeof(int) > 1: must be int-sized } — end example]
An empty-declaration has no effect.
Except where otherwise specified, the meaning of an attribute-declaration is implementation-defined.

9.2 Specifiers [dcl.spec]

9.2.1 General [dcl.spec.general]

The specifiers that can be used in a declaration are
The optional attribute-specifier-seq in a decl-specifier-seq appertains to the type determined by the preceding decl-specifiers ([dcl.meaning]).
The attribute-specifier-seq affects the type only for the declaration it appears in, not other declarations involving the same type.
At most one of each of the decl-specifiers friend, typedef, or inline shall appear in a decl-specifier-seq.
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 the decl-specifier-seq.
The sequence shall be self-consistent as described below.
[Example 1: typedef char* Pc; static Pc; // error: name missing
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 than const 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 f(char* const) (not const char*) void g(const int Pc); // void g(const int)
— end example]
[Note 1: 
Since signed, unsigned, long, and short by 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 h(unsigned int) void k(unsigned int Pc); // void k(unsigned int) — 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 given decl-specifier-seq, except that thread_local may appear with static or extern.
If thread_local appears in any declaration of a variable it shall be present in all declarations of that entity.
If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list or member-declarator-list of the declaration shall not be empty (except for an anonymous union declared in a namespace scope ([class.union.anon])).
The storage-class-specifier applies to the name declared by each init-declarator in the list and not to any names declared by other specifiers.
[Note 1: 
See [temp.expl.spec] and [temp.explicit] for restrictions in explicit specializations and explicit instantiations, respectively.
— end note]
[Note 2: 
A variable declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default.
— end note]
The thread_local specifier indicates that the named entity has thread storage duration ([basic.stc.thread]).
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 the storage-class-specifier static is implied if no other storage-class-specifier appears in the decl-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 no static function declarations within a block, nor any static function parameters.
A static specifier used in the declaration of a variable declares the variable to have static storage duration ([basic.stc.static]), unless accompanied by the thread_local specifier, which declares the variable to have thread storage duration ([basic.stc.thread]).
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].
[Note 3: 
The extern keyword can also be used in explicit-instantiations and linkage-specifications, but it is not a storage-class-specifier in such contexts.
— end note]
All declarations for a given entity shall give its name the same linkage.
[Note 4: 
The linkage given by some declarations is affected by previous declarations.
Overloads are distinct entities.
— end note]
[Example 1: static char* f(); // f() has internal linkage char* f() // f() still has internal linkage { /* ... */ } char* g(); // g() has external linkage static char* g() // error: inconsistent linkage { /* ... */ } void h(); inline void h(); // external linkage inline void l(); void l(); // external linkage inline void m(); extern void m(); // external linkage static void n(); inline void n(); // internal linkage static int a; // a has internal linkage int a; // error: two definitions static int b; // b has internal linkage extern int b; // b still has internal linkage int c; // c has external linkage static int c; // error: inconsistent linkage extern int d; // d has external linkage static int d; // error: inconsistent linkage — end example]
The name of a declared but undefined class can be used in an extern 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); // error: S is incomplete f(); // error: S is incomplete } — end example]
The mutable specifier shall appear only in the declaration of a non-static data member ([class.mem]) whose type is neither const-qualified nor a reference type.
[Example 3: class X { mutable const int* p; // OK mutable int* const q; // error }; — end example]
[Note 5: 
The mutable specifier on a class data member nullifies a const 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]

A function-specifier can be used only in a function declaration.
At most one explicit-specifier and at most one virtual keyword shall appear in a decl-specifier-seq.
The virtual specifier shall be used only in the initial declaration of a non-static member function; see [class.virtual].
An explicit-specifier shall be used only in the declaration of a constructor or conversion function within its class definition; see [class.conv.ctor] and [class.conv.fct].
In an explicit-specifier, the constant-expression, if supplied, shall be a contextually converted constant expression of type bool ([expr.const]).
The explicit-specifier explicit without a constant-expression is equivalent to the explicit-specifier explicit(true).
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.
[Example 1: struct S { explicit(sizeof(char[2])) S(char); // error: narrowing conversion of value 2 to type bool explicit(sizeof(char)) S(bool); // OK, conversion of value 1 to type bool is non-narrowing }; — end example]

9.2.4 The typedef specifier [dcl.typedef]

Declarations containing the decl-specifier typedef declare identifiers that can be used later for naming fundamental ([basic.fundamental]) or compound ([basic.compound]) types.
The typedef specifier shall not be combined in a decl-specifier-seq with any other kind of specifier except a defining-type-specifier, and it shall not be used in the decl-specifier-seq of a parameter-declaration ([dcl.fct]) nor in the decl-specifier-seq of a function-definition ([dcl.fct.def]).
If a typedef specifier appears in a declaration without a declarator, the program is ill-formed.
A name declared with the typedef specifier becomes a typedef-name.
A typedef-name names the type associated with the identifier ([dcl.decl]) or simple-template-id ([temp.pre]); a typedef-name is thus a synonym for another type.
A typedef-name does not introduce a new type the way a class declaration ([class.name]) or enum declaration ([dcl.enum]) does.
[Example 1: 
After typedef int MILES, *KLICKSP; the constructions MILES distance; extern KLICKSP metricp; are all correct declarations; the type of distance is int and that of metricp is “pointer to int.
— end example]
A typedef-name can also be introduced by an alias-declaration.
The identifier following the using keyword is not looked up; it becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name.
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); // redeclare ignore template<class T> struct P { }; using cell = P<cell*>; // error: cell not found ([basic.scope.pdecl]) — end example]
The defining-type-specifier-seq of the defining-type-id shall not define a class or enumeration if the alias-declaration is the declaration of a template-declaration.
A simple-template-id is only a typedef-name if its template-name names an alias template or a template template-parameter.
[Note 1: 
A simple-template-id that names a class template specialization is a class-name ([class.name]).
If a typedef-name is used to identify the subject of an elaborated-type-specifier ([dcl.type.elab]), a class definition, a constructor declaration, or a destructor declaration, the program is ill-formed.
— end note]
[Example 3: struct S { S(); ~S(); }; typedef struct S T; S a = T(); // OK struct T * p; // error — end example]
An unnamed class or enumeration C defined in a typedef declaration has the first typedef-name declared by the declaration to be of type C as its typedef name for linkage purposes ([basic.link]).
[Note 2: 
A typedef declaration involving a lambda-expression does not itself define the associated closure type, and so the closure type is not given a typedef name for linkage purposes.
— end note]
[Example 4: typedef struct { } *ps, S; // S is the typedef name for linkage purposes typedef decltype([]{}) C; // the closure type has no typedef name for linkage purposes — end example]
An unnamed class with a typedef name for linkage purposes shall not
  • declare any members other than non-static data members, member enumerations, or member classes,
  • have any base classes or default member initializers, or
  • contain a lambda-expression,
and all member classes shall also satisfy these requirements (recursively).
[Example 5: typedef struct { int f() {} } X; // error: struct with typedef name for linkage has member functions — end example]

9.2.5 The friend specifier [dcl.friend]

The friend specifier is used to specify access to class members; see [class.friend].

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 on its first declaration is implicitly an inline function or variable ([dcl.inline]).
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); // OK, declaration constexpr int bufsz = 1024; // OK, definition constexpr struct pixel { // error: pixel is a type int x; int y; constexpr pixel(int); // OK, declaration }; constexpr pixel::pixel(int a) : x(a), y(x) // OK, definition { square(x); } constexpr pixel small(2); // error: square not defined, so small(2) // not constant ([expr.const]) so constexpr not satisfied constexpr void square(int &x) { // OK, definition x *= x; } constexpr pixel large(4); // OK, square defined int next(constexpr int x) { // error: not for parameters return x + 1; } extern constexpr int memsz; // error: not a definition — end example]
A constexpr or consteval specifier used in the declaration of a function declares that function to be a constexpr function.
[Note 3: 
A function or constructor declared with the consteval specifier is an immediate function ([expr.const]).
— end note]
A destructor, an allocation function, or a deallocation function shall not be declared with the consteval specifier.
A function is constexpr-suitable if
Except for instantiated constexpr functions, non-templated constexpr functions shall be constexpr-suitable.
[Example 2: constexpr int square(int x) { return x * x; } // OK constexpr long long_max() { return 2147483647; } // OK constexpr int abs(int x) { if (x < 0) x = -x; return x; // OK } constexpr int constant_non_42(int n) { // OK if (n == 42) { static int value = n; return value; } return n; } constexpr int uninit() { struct { int a; } s; return s.a; // error: uninitialized read of s.a } constexpr int prev(int x) { return --x; } // OK constexpr int g(int x, int n) { // OK int r = 1; while (--n > 0) r *= x; return r; } — end example]
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_evaluated within an invocation of the function to produce a different value.
— end note]
[Note 5: 
It is possible to write a constexpr function for which no invocation satisfies the requirements of a core constant expression.
— end note]
The constexpr and consteval specifiers have no effect on the type of a constexpr function.
[Example 3: constexpr int bar(int x, int y) // OK { return x + y + x*y; } // ... int bar(int x, int y) // error: redefinition of bar { 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 ([expr.const]).
A constexpr variable that is an object, as well as any temporary to which a constexpr reference is bound, shall have constant destruction.
[Example 4: struct pixel { int x, y; }; constexpr pixel ur = { 1294, 1024 }; // OK constexpr pixel origin; // error: initializer missing — 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, even if the implementation would perform that initialization as a static initialization ([basic.start.static]).
[Note 1: 
The constinit specifier ensures that the variable is initialized during static initialization.
— 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); // OK constinit const char * d = f(false); // error — 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.
A function declaration ([dcl.fct], [class.mfct], [class.friend]) with an inline specifier declares an inline 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 an inline 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 can be defined in multiple translation units ([basic.def.odr]), but is one entity with one address.
A type or static variable defined in the body of such a function is therefore a single entity.
— 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]

As a general rule, at most one defining-type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a defining-type-specifier-seq, and at most one type-specifier is allowed in a type-specifier-seq.
The only exceptions to this rule are the following:
  • const can be combined with any type specifier except itself.
  • volatile can be combined with any type specifier except itself.
  • signed or unsigned can be combined with char, long, short, or int.
  • short or long can be combined with int.
  • long can be combined with double.
  • long can be combined with long.
Except in a declaration of a constructor, destructor, or conversion function, at least one defining-type-specifier that is not a cv-qualifier shall appear in a complete type-specifier-seq or a complete decl-specifier-seq.75
[Note 1: 
enum-specifiers, class-specifiers, and typename-specifiers are discussed in [dcl.enum], [class], and [temp.res], respectively.
The remaining type-specifiers are discussed in the rest of [dcl.type].
— end note]
75)75)
There is no special provision for a decl-specifier-seq that lacks a type-specifier or that has a type-specifier that only specifies cv-qualifiers.
The “implicit int” rule of C is no longer supported.

9.2.9.2 The cv-qualifiers [dcl.type.cv]

There are two cv-qualifiers, const and volatile.
Each cv-qualifier shall appear at most once in a cv-qualifier-seq.
If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list or member-declarator-list of the declaration shall not be empty.
[Note 1: 
[basic.type.qualifier] and [dcl.fct] describe how cv-qualifiers affect object and function types.
— end note]
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 ([expr.const]).
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]
Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.
[Example 1: const int ci = 3; // cv-qualified (initialized as required) ci = 4; // error: attempt to modify const int i = 2; // not cv-qualified const int* cip; // pointer to const int cip = &i; // OK, cv-qualified access path to unqualified *cip = 4; // error: attempt to modify through ptr to const int* ip; ip = const_cast<int*>(cip); // cast needed to convert const int* to int* *ip = 4; // defined: *ip points to i, a non-const object const int* ciq = new const int (3); // initialized as required int* iq = const_cast<int*>(ciq); // cast required *iq = 4; // undefined behavior: modifies a const object
For another example, struct X { mutable int i; int j; }; struct Y { X x; Y(); }; const Y y; y.x.i++; // well-formed: mutable member can be modified y.x.j++; // error: const-qualified member modified Y* p = const_cast<Y*>(&y); // cast away const-ness of y p->x.i = 99; // well-formed: mutable member can be modified p->x.j = 99; // undefined behavior: modifies a const subobject
— end example]
The semantics of an access through a volatile glvalue are implementation-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 it is possible for the value of the object to change by means undetectable by an implementation.
Furthermore, for some implementations, volatile can indicate that special hardware instructions are needed to access the object.
See [intro.execution] for detailed semantics.
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 component name of a type-name is the first name in it.
A placeholder-type-specifier is a placeholder for a type to be deduced ([dcl.spec.auto]).
A type-specifier of the form typename nested-name-specifier template-name is a placeholder for a deduced class type ([dcl.type.class.deduct]).
The nested-name-specifier, if any, shall be non-dependent and the template-name shall name a deducible template.
A deducible template is either a class template or is an alias template whose defining-type-id is of the form
typename nested-name-specifier template simple-template-id
where the nested-name-specifier (if any) is non-dependent and the template-name of the simple-template-id names a deducible template.
[Note 1: 
An injected-class-name is never interpreted as a template-name in contexts where class template argument deduction would be performed ([temp.local]).
— end note]
The other simple-type-specifiers specify either a previously-declared type, a type determined from an expression, or one of the fundamental types ([basic.fundamental]).
Table 17 summarizes the valid combinations of simple-type-specifiers and the types they specify.
Table 17: simple-type-specifiers and the types they specify [tab:dcl.type.simple]
Specifier(s)
Type
the type named
the type as defined in [temp.names]
the type as defined in [dcl.type.decltype]
the type as defined in [dcl.type.pack.index]
the type as defined in [dcl.spec.auto]
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 Pack indexing specifier [dcl.type.pack.index]

The typedef-name P in a pack-index-specifier shall denote a pack.
The constant-expression shall be a converted constant expression ([expr.const]) of type std​::​size_t whose value V, termed the index, is such that .
[Note 1: 
The pack-index-specifier denotes the type of the element of the pack.
— end note]

9.2.9.5 Elaborated type specifiers [dcl.type.elab]

The component names of an elaborated-type-specifier are its identifier (if any) and those of its nested-name-specifier and simple-template-id (if any).
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]), a partial specialization ([temp.spec.partial]), an explicit instantiation ([temp.explicit]), or it has one of the following forms:
In the first case, the elaborated-type-specifier declares the identifier as a class-name.
The second case shall appear only in an explicit-specialization ([temp.expl.spec]) or in a template-declaration (where it declares a partial specialization).
The attribute-specifier-seq, if any, appertains to the class or template being declared.
Otherwise, an elaborated-type-specifier E shall not have an attribute-specifier-seq.
If E contains an identifier but no nested-name-specifier and (unqualified) lookup for the identifier finds nothing, E shall not be introduced by the enum keyword and declares the identifier as a class-name.
The target scope of E is the nearest enclosing namespace or block scope.
A friend-type-specifier that is an elaborated-type-specifier shall have one of the following forms: Any unqualified lookup for the identifier (in the first case) does not consider scopes that contain the nearest enclosing namespace or block scope; no name is bound.
[Note 1: 
A using-directive in the target scope is ignored if it refers to a namespace not contained by that scope.
— end note]
[Note 2: 
[basic.lookup.elab] describes how name lookup proceeds in an elaborated-type-specifier.
An elaborated-type-specifier can be used to refer to a previously declared class-name or enum-name even if the name has been hidden by a non-type declaration.
— end note]
If the identifier or simple-template-id in an elaborated-type-specifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name ([dcl.type.simple]).
[Note 3: 
This implies that, within a class template with a template type-parameter T, the declaration friend class T; is ill-formed.
However, the similar declaration friend T; is well-formed ([class.friend]).
— end note]
The class-key or enum keyword present in an elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers.
This rule also applies to the form of elaborated-type-specifier that declares a class-name or friend class since it can be construed as referring to the definition of the class.
Thus, in any elaborated-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; // OK struct S { } s; class S* p = &s; // OK — end example]

9.2.9.6 Decltype specifiers [dcl.type.decltype]

For an expression E, the type denoted by decltype(E) is defined as follows:
The operand of the decltype specifier is an unevaluated operand.
[Example 1: const int&& foo(); int i; struct A { double x; }; const A* a = new A(); decltype(foo()) x1 = 17; // type is const int&& decltype(i) x2; // type is int decltype(a->x) x3; // type is double decltype((a->x)) x4 = x3; // type is const double& void f() { [](auto ...pack) { decltype(pack...[0]) x5; // type is int decltype((pack...[0])) x6; // type is int& }(0); } — 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, a decltype-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) // identity -> T; template<class T> auto f(T) // #1 -> decltype(i(h<T>())); // forces completion of A<T> and implicitly uses A<T>​::​~A() // for the temporary introduced by the use of h(). // (A temporary is not introduced as a result of the use of i().) template<class T> auto f(T) // #2 -> void; auto g() -> void { f(42); // OK, calls #2. (#1 is not a viable candidate: type deduction // fails ([temp.deduct]) because A<int>​::​~A() is implicitly used in its // decltype-specifier) } template<class T> auto q(T) -> decltype((h<T>())); // does not force completion of A<T>; A<T>​::​~A() is not implicitly // used within the context of this decltype-specifier void r() { q(42); // error: deduction against q succeeds, so overload resolution selects // the specialization “q(T) -> decltype((h<T>()))'' with Tint; // the return type is A<int>, so a temporary is introduced and its // destructor is used, so the program is ill-formed } — end example]

9.2.9.7 Placeholder type specifiers [dcl.spec.auto]

9.2.9.7.1 General [dcl.spec.auto.general]

A placeholder-type-specifier designates a placeholder type that will be replaced later, typically by deduction from an initializer.
The type of a parameter-declaration of a function declaration ([dcl.fct]), lambda-expression ([expr.prim.lambda]), or template-parameter ([temp.param]) can be declared using a placeholder-type-specifier of the form type-constraint auto.
The placeholder type shall appear as one of the decl-specifiers in the decl-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a decl-specifier (see below); the placeholder type is a generic parameter type placeholder of the function declaration, lambda-expression, or template-parameter, respectively.
[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]
A placeholder type can appear in the decl-specifier-seq for a function declarator that includes a trailing-return-type ([dcl.fct]).
A placeholder type can appear in the decl-specifier-seq or type-specifier-seq in the declared return type of a function declarator that declares a function; 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 the decl-specifiers in the decl-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a decl-specifier; the decl-specifier-seq shall be followed by one or more declarators, each of which shall be followed by a non-empty initializer.
[Example 1: 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 (*fp)() -> auto = f; // OK auto h(); // OK, h's return type will be deduced when it is defined — end example]
The auto type-specifier can also be used to introduce a structured binding declaration ([dcl.struct.bind]).
A placeholder type can also be used in the type-specifier-seq of the new-type-id or in the type-id of a new-expression ([expr.new]).
In such a type-id, the placeholder type shall appear as one of the type-specifiers in the type-specifier-seq or as one of the type-specifiers in a trailing-return-type that specifies the type that replaces such a type-specifier.
The auto type-specifier can also be used as the simple-type-specifier in an explicit type conversion (functional notation) ([expr.type.conv]).
A program that uses a placeholder type in a context not explicitly allowed in [dcl.spec.auto] is ill-formed.
If the init-declarator-list contains more than one init-declarator, they shall all form declarations of variables.
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; // OK, auto is int auto a = 5, b = { 1, 2 }; // error: different types for auto — 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 a return statement with no operand at the closing brace of the function body.
[Example 3: auto f() { } // OK, return type is void auto* g() { } // error: cannot deduce auto* from void() — 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 a variable or function with an undeduced placeholder type is named by an expression ([basic.def.odr]), 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 other return statements.
[Example 4: auto n = n; // error: n's initializer refers to n 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 templated function 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; } // 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 fs to determine return types, // chooses second — end example]
If a function or function template F has a declared return type that uses a placeholder type, redeclarations or specializations of F shall use that placeholder type, not a deduced type; otherwise, they shall not use a placeholder type.
[Example 6: auto f(); auto f() { return 42; } // return type is int auto f(); // OK int f(); // error: auto and int don't match 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> extern int v; auto v = 17; // OK, redeclares v struct S { static int i; }; auto S::i = 23; // OK — end example]
A function declared with a return type that uses a placeholder type shall not be virtual ([class.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); // 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]

9.2.9.7.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-clause E, are determined as follows:
T shall not be an array type.
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.
If the initialization is copy-list-initialization, a declaration of std​::​initializer_list shall precede ([basic.lookup.general]) the placeholder-type-specifier.
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) auto f1(int x) -> decltype((x)) { return (x); } // return type is int& auto f2(int x) -> decltype(auto) { return (x); } // return type is int&& — 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.

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

If a placeholder for a deduced class type appears as a decl-specifier in the decl-specifier-seq of 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]{}; // error: no declarator operators allowed const A& y{}; // error: no declarator operators allowed — 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]).
If the decl-specifier-seq is followed by an init-declarator-list or member-declarator-list containing more than one declarator, the type that replaces the placeholder shall be the same in each deduction.
A placeholder for a deduced class type can also be used in the type-specifier-seq in the new-type-id or type-id of a new-expression ([expr.new]), as the simple-type-specifier in an explicit type conversion (functional notation), or as the type-specifier in the parameter-declaration of a template-parameter ([temp.param]).
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); // OK, deduces int for T auto d = container(v.begin(), v.end()); // OK, deduces double for T container e{5, 6}; // error: int is not an iterator — end example]

9.3 Declarators [dcl.decl]

9.3.1 General [dcl.decl.general]

A declarator declares a single variable, function, or type, within a declaration.
The init-declarator-list appearing in a simple-declaration is a comma-separated sequence of declarators, each of which can have an initializer.
In all contexts, a declarator is interpreted as given below.
Where an abstract-declarator can be used (or omitted) in place of a declarator ([dcl.fct], [except.pre]), it is as if a unique identifier were included in the appropriate place ([dcl.name]).
The preceding specifiers indicate the type, storage duration, linkage, or other properties of the entity or entities being declared.
Each declarator specifies one entity and (optionally) names it and/or modifies the type of the specifiers with operators such as * (pointer to) and () (function returning).
[Note 1: 
An init-declarator can also specify an initializer ([dcl.init]).
— end note]
Each init-declarator or member-declarator in a declaration is analyzed separately as if it were in a declaration by itself.
[Note 2: 
A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator.
That is, T D1, D2, ... Dn; is usually equivalent to T D1; T D2; ... T Dn; where T is a decl-specifier-seq and each Di is an init-declarator or member-declarator.
One exception is when a name introduced by one of the declarators hides a type name used by the decl-specifiers, so that when the same decl-specifiers are used in a subsequent declaration, they do not have the same meaning, as in struct S { /* ... */ }; S S, T; // declare two instances of struct S which is not equivalent to struct S { /* ... */ }; S S; S T; // error
Another exception is when T is auto ([dcl.spec.auto]), for example: auto i = 1, j = 2.0; // error: deduced types for i and j do not match as opposed to auto i = 1; // OK, i deduced to have type int auto j = 2.0; // OK, j deduced to have type double
— end note]
The optional requires-clause in an init-declarator or member-declarator shall be present only if the declarator declares a templated function ([temp.pre]).
When present after a declarator, the requires-clause is called the trailing requires-clause.
The trailing requires-clause introduces the constraint-expression that results from interpreting its constraint-logical-or-expression as a constraint-expression.
[Example 1: void f1(int a) requires true; // error: non-templated function template<typename T> auto f2(T a) -> bool requires true; // OK template<typename T> auto f3(T a) requires true -> bool; // error: requires-clause precedes trailing-return-type void (*pf)() requires true; // error: constraint on a variable void g(int (*)() requires true); // error: constraint on a parameter-declaration auto* p = new void(*)(char) requires true; // error: not a function declaration — end example]

9.3.2 Type names [dcl.name]

To specify type conversions explicitly, and as an argument of sizeof, alignof, new, or typeid, the name of a type shall be specified.
This can be done with a type-id or new-type-id ([expr.new]), which is syntactically a declaration for a variable or function of that type that omits the name of the entity.
It is possible to identify uniquely the location in the abstract-declarator where the identifier would appear if the construction were a declarator in a declaration.
The named type is then the same as the type of the hypothetical identifier.
[Example 1: 
int // int i int * // int *pi int *[3] // int *p[3] int (*)[3] // int (*p3i)[3] int *() // int *f() int (*)(double) // int (*pf)(double) name respectively the types “int”, “pointer to int”, “array of 3 pointers to int”, “pointer to array of 3 int”, “function of (no parameters) returning pointer to int”, and “pointer to a function of (double) returning int.
— end example]
[Note 1: 
A type can also be named by a typedef-name, which is introduced by a typedef declaration or alias-declaration ([dcl.typedef]).
— end note]

9.3.3 Ambiguity resolution [dcl.ambig.res]

The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in [stmt.ambig] can also occur in the context of a declaration.
In that context, the choice is between an object declaration with a function-style cast as the initializer and a declaration involving a function declarator with a redundant set of parentheses around a parameter name.
Just as for the ambiguities mentioned in [stmt.ambig], the resolution is to consider any construct, such as the potential parameter declaration, that could possibly be a declaration to be a declaration.
However, a construct that can syntactically be a declaration whose outermost declarator would match the grammar of a declarator with a trailing-return-type is a declaration only if it starts with auto.
[Note 1: 
A declaration can be explicitly disambiguated by adding parentheses around the argument.
The ambiguity can be avoided by use of copy-initialization or list-initialization syntax, or by use of a non-function-style cast.
— end note]
[Example 1: struct S { S(int); }; typedef struct BB { int C[2]; } *B, C; void foo(double a) { S v(int(a)); // function declaration S w(int()); // function declaration S x((int(a))); // object declaration S y((int)a); // object declaration S z = int(a); // object declaration S a(B()->C); // object declaration S b(auto()->C); // function declaration } — end example]
An ambiguity can arise from the similarity between a function-style cast and a type-id.
The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
However, a construct that can syntactically be a type-id whose outermost abstract-declarator would match the grammar of an abstract-declarator with a trailing-return-type is considered a type-id only if it starts with auto.
[Example 2: template <class T> struct X {}; template <int N> struct Y {}; X<int()> a; // type-id X<int(1)> b; // expression (ill-formed) Y<int()> c; // type-id (ill-formed) Y<int(1)> d; // expression void foo(signed char a) { sizeof(int()); // type-id (ill-formed) sizeof(int(a)); // expression sizeof(int(unsigned(a))); // type-id (ill-formed) (int())+1; // type-id (ill-formed) (int(a))+1; // expression (int(unsigned(a)))+1; // type-id (ill-formed) } typedef struct BB { int C[2]; } *B, C; void g() { sizeof(B()->C[1]); // OK, sizeof(expression) sizeof(auto()->C[1]); // error: sizeof of a function returning an array } — end example]
Another ambiguity arises in a parameter-declaration-clause when a type-name is nested in parentheses.
In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id.
The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id.
[Example 3: class C { }; void f(int(C)) { } // void f(int(*fp)(C c)) { } // not: void f(int C) { } int g(C); void foo() { f(1); // error: cannot convert 1 to function pointer f(g); // OK }
For another example, class C { }; void h(int *(C[10])); // void h(int *(*_fp)(C _parm[10])); // not: void h(int *C[10]);
— end example]

9.3.4 Meaning of declarators [dcl.meaning]

9.3.4.1 General [dcl.meaning.general]

A declarator contains exactly one declarator-id; it names the entity that is declared.
[Note 1: 
An unqualified-id that is not an identifier is used to declare certain functions ([class.conv.fct], [class.dtor], [over.oper], [over.literal]).
— end note]
The optional attribute-specifier-seq following a declarator-id appertains to the entity that is declared.
If the declaration is a friend declaration:
  • The declarator does not bind a name.
  • If the id-expression E in the declarator-id of the declarator is a qualified-id or a template-id:
    • If the friend declaration is not a template declaration, then in the lookup for the terminal name of E:
    • The declarator shall correspond to one or more declarations found by the lookup; they shall all have the same target scope, and the target scope of the declarator is that scope.
  • Otherwise, the terminal name of E is not looked up.
    The declaration's target scope is the innermost enclosing namespace scope; if the declaration is contained by a block scope, the declaration shall correspond to a reachable ([module.reach]) declaration that inhabits the innermost block scope.
Otherwise:
  • If the id-expression in the declarator-id of the declarator is a qualified-id Q, let S be its lookup context ([basic.lookup.qual]); the declaration shall inhabit a namespace scope.
  • Otherwise, let S be the entity associated with the scope inhabited by the declarator.
  • If the declarator declares an explicit instantiation or a partial or explicit specialization, the declarator does not bind a name.
    If it declares a class member, the terminal name of the declarator-id is not looked up; otherwise, only those lookup results that are nominable in S are considered when identifying any function template specialization being declared ([temp.deduct.decl]).
    [Example 1: namespace N { inline namespace O { template<class T> void f(T); // #1 template<class T> void g(T) {} } namespace P { template<class T> void f(T*); // #2, more specialized than #1 template<class> int g; } using P::f,P::g; } template<> void N::f(int*) {} // OK, #2 is not nominable template void N::g(int); // error: lookup is ambiguous — end example]
  • Otherwise, the terminal name of the declarator-id is not looked up.
    If it is a qualified name, the declarator shall correspond to one or more declarations nominable in S; all the declarations shall have the same target scope and the target scope of the declarator is that scope.
    [Example 2: namespace Q { namespace V { void f(); } void V::f() { /* ... */ } // OK void V::g() { /* ... */ } // error: g() is not yet a member of V namespace V { void g(); } } namespace R { void Q::V::g() { /* ... */ } // error: R doesn't enclose Q } — end example]
  • If the declaration inhabits a block scope S and declares a function ([dcl.fct]) or uses the extern specifier, the declaration shall not be attached to a named module ([module.unit]); its target scope is the innermost enclosing namespace scope, but the name is bound in S.
    [Example 3: namespace X { void p() { q(); // error: q not yet declared extern void q(); // q is a member of namespace X extern void r(); // r is a member of namespace X } void middle() { q(); // error: q not found } void q() { /* ... */ } // definition of X​::​q } void q() { /* ... */ } // some other, unrelated q void X::r() { /* ... */ } // error: r cannot be declared by qualified-id — end example]
A static, thread_local, extern, mutable, friend, inline, virtual, constexpr, consteval, constinit, or typedef specifier or an explicit-specifier applies directly to each declarator-id in a declaration; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.
Thus, (for each declarator) a declaration has the form T D where T is of the form attribute-specifier-seq decl-specifier-seq and D is a declarator.
Following is a recursive procedure for determining the type specified for the contained declarator-id by such a declaration.
First, the decl-specifier-seq determines a type.
In a declaration T D the decl-specifier-seq T determines the type T.
[Example 4: 
In the declaration int unsigned i; the type specifiers int unsigned determine the type “unsigned int” ([dcl.type.simple]).
— end example]
In a declaration attribute-specifier-seq T D where D is an unadorned declarator-id, the type of the declared entity is “T.
In a declaration T D where D has the form
( D1 )
the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration T D1
Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.

9.3.4.2 Pointers [dcl.ptr]

In a declaration T D where D has the form and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list cv-qualifier-seq pointer to T.
The cv-qualifiers apply to the pointer and not to the object pointed to.
Similarly, the optional attribute-specifier-seq ([dcl.attr.grammar]) appertains to the pointer and not to the object pointed to.
[Example 1: 
The declarations const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; int i, *p, *const cp = &i; declare ci, a constant integer; pc, a pointer to a constant integer; cpc, a constant pointer to a constant integer; ppc, a pointer to a pointer to a constant integer; i, an integer; p, a pointer to integer; and cp, a constant pointer to integer.
The value of ci, cpc, and cp cannot be changed after initialization.
The value of pc can be changed, and so can the object pointed to by cp.
Examples of some correct operations are i = ci; *cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;
Examples of ill-formed operations are ci = 1; // error ci++; // error *pc = 2; // error cp = &ci; // error cpc++; // error p = pc; // error ppc = &p; // error
Each is unacceptable because it would either change the value of an object declared const or allow it to be changed through a cv-unqualified pointer later, for example: *ppc = &ci; // OK, but would make p point to ci because of previous error *p = 5; // clobber ci
— end example]
[Note 1: 
Forming a pointer to reference type is ill-formed; see [dcl.ref].
Forming a function pointer type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see [dcl.fct].
Since the address of a bit-field ([class.bit]) cannot be taken, a pointer can never point to a bit-field.
— end note]

9.3.4.3 References [dcl.ref]

In a declaration T D where D has either of the forms and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list reference to T.
The optional attribute-specifier-seq appertains to the reference type.
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.decltype]), in which case the cv-qualifiers are ignored.
[Example 1: typedef int& A; const A aref = 3; // error: lvalue reference to non-const initialized with rvalue
The type of aref is “lvalue reference to int”, not “lvalue reference to const int.
— end example]
[Note 1: 
A reference can be thought of as a name of an object.
— end note]
Forming the type “reference to cv void” is ill-formed.
A reference type that is declared using & is called an lvalue reference, and a reference type that is declared using && is called an rvalue reference.
Lvalue references and rvalue references are distinct types.
Except where explicitly noted, they are semantically equivalent and commonly referred to as references.
[Example 2: 
void f(double& a) { a += 3.14; } // ... double d = 0; f(d); declares a to be a reference parameter of f so the call f(d) will add 3.14 to d.
int v[20]; // ... int& g(int i) { return v[i]; } // ... g(3) = 7; declares the function g() to return a reference to an integer so g(3)=7 will assign 7 to the fourth element of the array v.
For another example, struct link { link* next; }; link* first; void h(link*& p) { // p is a reference to pointer p->next = first; first = p; p = 0; } void k() { link* q = new link; h(q); } declares p to be a reference to a pointer to link so h(q) will leave q with the value zero.
— end example]
It is unspecified whether or not a reference requires storage ([basic.stc]).
There shall be no references to references, no arrays of references, and no pointers to references.
The declaration of a reference shall contain an initializer ([dcl.init.ref]) except when the declaration contains an explicit extern specifier ([dcl.stc]), is a class member ([class.mem]) declaration within a class definition, or is the declaration of a parameter or a return type ([dcl.fct]); see [basic.def].
Attempting to bind a reference to a function where the converted initializer is a glvalue whose type is not call-compatible ([expr.call]) with the type of the function's definition results in undefined behavior.
Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible ([basic.lval]) results in undefined behavior.
[Note 2: 
The object designated by such a glvalue can be outside its lifetime ([basic.life]).
Because a null pointer value or a pointer past the end of an object does not point to an object, a reference in a well-defined program cannot refer to such things; see [expr.unary.op].
As described in [class.bit], a reference cannot be bound directly to a bit-field.
— end note]
The behavior of an evaluation of a reference ([expr.prim.id], [expr.ref]) that does not happen after ([intro.races]) the initialization of the reference is undefined.
[Example 3: int &f(int&); int &g(); extern int &ir3; int *ip = 0; int &ir1 = *ip; // undefined behavior: null pointer int &ir2 = f(ir3); // undefined behavior: ir3 not yet initialized int &ir3 = g(); int &ir4 = f(ir4); // undefined behavior: ir4 used in its own initializer char x alignas(int); int &ir5 = *reinterpret_cast<int *>(&x); // undefined behavior: initializer refers to char object — end example]
If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.decltype]) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR.
[Note 3: 
This rule is known as reference collapsing.
— end note]
[Example 4: int i; typedef int& LRI; typedef int&& RRI; LRI& r1 = i; // r1 has the type int& const LRI& r2 = i; // r2 has the type int& const LRI&& r3 = i; // r3 has the type int& RRI& r4 = i; // r4 has the type int& RRI&& r5 = 5; // r5 has the type int&& decltype(r2)& r6 = i; // r6 has the type int& decltype(r2)&& r7 = i; // r7 has the type int& — end example]
[Note 4: 
Forming a reference to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see [dcl.fct].
— end note]

9.3.4.4 Pointers to members [dcl.mptr]

The component names of a ptr-operator are those of its nested-name-specifier, if any.
In a declaration T D where D has the form and the nested-name-specifier denotes a class, and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list cv-qualifier-seq pointer to member of class nested-name-specifier of type T.
The optional attribute-specifier-seq ([dcl.attr.grammar]) appertains to the pointer-to-member.
[Example 1: 
struct X { void f(int); int a; }; struct Y; int X::* pmi = &X::a; void (X::* pmf)(int) = &X::f; double X::* pmd; char Y::* pmc; declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member of X of type double and a pointer to a member of Y of type char respectively.
The declaration of pmd is well-formed even though X has no members of type double.
Similarly, the declaration of pmc is well-formed even though Y is an incomplete type.
pmi and pmf can be used like this: X obj; // ... obj.*pmi = 7; // assign 7 to an integer member of obj (obj.*pmf)(7); // call a function member of obj with the argument 7
— end example]
A pointer to member shall not point to a static member of a class ([class.static]), a member with reference type, or “cv void.
[Note 1: 
The type “pointer to member” is distinct from the type “pointer”, that is, a pointer to member is declared only by the pointer-to-member declarator syntax, and never by the pointer declarator syntax.
There is no “reference-to-member” type in C++.
— end note]

9.3.4.5 Arrays [dcl.array]

In a declaration T D where D has the form and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of N T.
The constant-expression shall be a converted constant expression of type std​::​size_t ([expr.const]).
Its value N specifies the array bound, i.e., the number of elements in the array; N shall be greater than zero.
In a declaration T D where D has the form and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of unknown bound of T”, except as specified below.
A type of the form “array of N U” or “array of unknown bound of U” is an array type.
The optional attribute-specifier-seq appertains to the array type.
U is called the array element type; this type shall not be a reference type, a function type, an array of unknown bound, or cv void.
[Note 1: 
An array can be constructed from one of the fundamental types (except void), from a pointer, from a pointer to member, from a class, from an enumeration type, or from an array of known bound.
— end note]
[Example 1: 
float fa[17], *afp[17]; declares an array of float numbers and an array of pointers to float numbers.
— end example]
Any type of the form “cv-qualifier-seq array of N U” is adjusted to “array of N cv-qualifier-seq U”, and similarly for “array of unknown bound of U.
[Example 2: typedef int A[5], AA[2][3]; typedef const A CA; // type is “array of 5 const int'' typedef const AA CAA; // type is “array of 2 array of 3 const int'' — end example]
[Note 2: 
An “array of N cv-qualifier-seq U” has cv-qualified type; see [basic.type.qualifier].
— end note]
An object of type “array of N U” consists of a contiguously allocated non-empty set of N subobjects of type U, known as the elements of the array, and numbered 0 to N-1.
In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter ([dcl.fct]).
An array bound may also be omitted when an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer ([dcl.init], [class.mem], [expr.type.conv], [expr.new]).
In these cases, the array bound is calculated from the number of initial elements (say, N) supplied ([dcl.init.aggr]), and the type of the array is “array of N U.
Furthermore, if there is a reachable declaration of the entity that inhabits the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.
[Example 3: extern int x[10]; struct S { static int y[10]; }; int x[]; // OK, bound is 10 int S::y[]; // OK, bound is 10 void f() { extern int x[]; int i = sizeof(x); // error: incomplete object type } — end example]
[Note 3: 
When several “array of” specifications are adjacent, a multidimensional array type is created; only the first of the constant expressions that specify the bounds of the arrays can be omitted.
[Example 4: 
int x3d[3][5][7]; declares an array of three elements, each of which is an array of five elements, each of which is an array of seven integers.
The overall array can be viewed as a three-dimensional array of integers, with rank 3 ×5 ×7.
Any of the expressions x3d, x3d[i], x3d[i][j], x3d[i][j][k] can reasonably appear in an expression.
The expression x3d[i] is equivalent to *(x3d + i); in that expression, x3d is subject to the array-to-pointer conversion ([conv.array]) and is first converted to a pointer to a 2-dimensional array with rank 5 ×7 that points to the first element of x3d.
Then i is added, which on typical implementations involves multiplying i by the length of the object to which the pointer points, which is sizeof(int)×5 ×7.
The result of the addition and indirection is an lvalue denoting the array element of x3d (an array of five arrays of seven integers).
If there is another subscript, the same argument applies again, so x3d[i][j] is an lvalue denoting the array element of the array element of x3d (an array of seven integers), and x3d[i][j][k] is an lvalue denoting the array element of the array element of the array element of x3d (an integer).
— end example]
The first subscript in the declaration helps determine the amount of storage consumed by an array but plays no other part in subscript calculations.
— end note]
[Note 4: 
Conversions affecting expressions of array type are described in [conv.array].
— end note]
[Note 5: 
The subscript operator can be overloaded for a class ([over.sub]).
For the operator's built-in meaning, see [expr.sub].
— end note]

9.3.4.6 Functions [dcl.fct]

In a declaration T D where T may be empty and D has the form a derived-declarator-type-list is determined as follows:
The declared return type U of the function type is determined as follows:
The type of the declarator-id in D is “derived-declarator-type-list noexcept function of parameter-type-list cv-qualifier-seq ref-qualifier returning U”, where
Such a type is a function type.76
The optional attribute-specifier-seq appertains to the function type.
The parameter-declaration-clause determines the arguments that can be specified, and their processing, when the function is called.
[Note 1: 
The parameter-declaration-clause is used to convert the arguments specified on the function call; see [expr.call].
— end note]
If the parameter-declaration-clause is empty, the function takes no arguments.
A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list.
Except for this special case, a parameter shall not have type cv void.
A parameter with volatile-qualified type is deprecated; see [depr.volatile.type].
If the parameter-declaration-clause terminates with an ellipsis or a function parameter pack ([temp.variadic]), the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument and are not function parameter packs.
Where syntactically correct and where “...” is not part of an abstract-declarator, “, ...” is synonymous with “....
[Example 1: 
The declaration int printf(const char*, ...); declares a function that can be called with varying numbers and types of arguments.
printf("hello world"); printf("a=%d b=%d", a, b);
However, the first argument must be of a type that can be converted to a const char*.
— end example]
[Note 2: 
The standard header <cstdarg> contains a mechanism for accessing arguments passed using the ellipsis (see [expr.call] and [support.runtime]).
— end note]
The type of a function is determined using the following rules.
The type of each parameter (including function parameter packs) is determined from its own parameter-declaration ([dcl.decl]).
After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T.
After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.
The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list.
[Note 3: 
This transformation does not affect the types of the parameters.
For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types.
— end note]
[Example 2: void f(char*); // #1 void f(char[]) {} // defines #1 void f(const char*) {} // OK, another overload void f(char *const) {} // error: redefines #1 void g(char(*)[2]); // #2 void g(char[3][2]) {} // defines #2 void g(char[3][3]) {} // OK, another overload void h(int x(const int)); // #3 void h(int (*)(int)) {} // defines #3 — end example]
An explicit-object-parameter-declaration shall appear only as the first parameter-declaration of a parameter-declaration-list of one of:
A member-declarator with an explicit-object-parameter-declaration shall not include a ref-qualifier or a cv-qualifier-seq and shall not be declared static or virtual.
[Example 3: struct C { void f(this C& self); template <typename Self> void g(this Self&& self, int); void h(this C) const; // error: const not allowed here }; void test(C c) { c.f(); // OK, calls C​::​f c.g(42); // OK, calls C​::​g<C&> std::move(c).g(42); // OK, calls C​::​g<C> } — end example]
A function parameter declared with an explicit-object-parameter-declaration is an explicit object parameter.
An explicit object parameter shall not be a function parameter pack ([temp.variadic]).
An explicit object member function is a non-static member function with an explicit object parameter.
An implicit object member function is a non-static member function without an explicit object parameter.
The object parameter of a non-static member function is either the explicit object parameter or the implicit object parameter ([over.match.funcs]).
A non-object parameter is a function parameter that is not the explicit object parameter.
The non-object-parameter-type-list of a member function is the parameter-type-list of that function with the explicit object parameter, if any, omitted.
[Note 4: 
The non-object-parameter-type-list consists of the adjusted types of all the non-object parameters.
— end note]
A function type with a cv-qualifier-seq or a ref-qualifier (including a type named by typedef-name ([dcl.typedef], [temp.param])) shall appear only as:
[Example 4: typedef int FIC(int) const; FIC f; // error: does not declare a member function struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK — end example]
The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type.
In the latter case, the cv-qualifiers are ignored.
[Note 5: 
A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types.
— end note]
[Example 5: typedef void F(); struct S { const F f; // OK, equivalent to: void f(); }; — end example]
The return type, the parameter-type-list, the ref-qualifier, the cv-qualifier-seq, and the exception specification, but not the default arguments ([dcl.fct.default]) or the trailing requires-clause ([dcl.decl]), are part of the function type.
[Note 6: 
Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions.
— end note]
[Example 6: 
The declaration int fseek(FILE*, long, int); declares a function taking three arguments of the specified types, and returning int ([dcl.type]).
— end example]
[Note 7: 
A single name can be used for several different functions in a single scope; this is function overloading ([over]).
— end note]
The return type shall be a non-array object type, a reference type, or cv void.
[Note 8: 
An array of placeholder type is considered an array type.
— end note]
A volatile-qualified return type is deprecated; see [depr.volatile.type].
Types shall not be defined in return or parameter types.
A typedef of function type may be used to declare a function but shall not be used to define a function ([dcl.fct.def]).
[Example 7: typedef void F(); F fv; // OK, equivalent to void fv(); F fv { } // error void fv() { } // OK, definition of fv — end example]
An identifier can optionally be provided as a parameter name; if present in a function definition ([dcl.fct.def]), it names a parameter.
[Note 9: 
In particular, parameter names are also optional in function definitions and names used for a parameter in different declarations and the definition of a function need not be the same.
— end note]
[Example 8: 
The declaration int i, *pi, f(), *fpi(int), (*pif)(const char*, const char*), (*fpif(int))(int); declares an integer i, a pointer pi to an integer, a function f taking no arguments and returning an integer, a function fpi taking an integer argument and returning a pointer to an integer, a pointer pif to a function which takes two pointers to constant characters and returns an integer, a function fpif taking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer.
It is especially useful to compare fpi and pif.
The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through the (pointer) result to yield an integer.
In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through a pointer to a function yields a function, which is then called.
— end example]
[Note 10: 
Typedefs and trailing-return-types are sometimes convenient when the return type of a function is complex.
For example, the function fpif above can be declared typedef int IFUNC(int); IFUNC* fpif(int); or auto fpif(int)->int(*)(int);
A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id: template <class T, class U> auto add(T t, U u) -> decltype(t + u); rather than template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
— end note]
A non-template function is a function that is not a function template specialization.
[Note 11: 
A function template is not a function.
— end note]
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]).
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance.
For a placeholder-type-specifier of the form auto, the invented parameter is an unconstrained type-parameter.
For a placeholder-type-specifier of the form type-constraint auto, the invented parameter is a type-parameter with that type-constraint.
The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack.
If the placeholder contains decltype(auto), the program is ill-formed.
The adjusted function parameters of an abbreviated function template are derived from the parameter-declaration-clause by replacing each occurrence of a placeholder with the name of the corresponding invented template-parameter.
[Example 9: template<typename T> concept C1 = /* ... */; template<typename T> concept C2 = /* ... */; template<typename... Ts> concept C3 = /* ... */; void g1(const C1 auto*, C2 auto&); void g2(C1 auto&...); void g3(C3 auto...); void g4(C3 auto);
The declarations above are functionally equivalent (but not equivalent) to their respective declarations below: template<C1 T, C2 U> void g1(const T*, U&); template<C1... Ts> void g2(Ts&...); template<C3... Ts> void g3(Ts...); template<C3 T> void g4(T);
Abbreviated function templates can be specialized like all function templates.
template<> void g1<int>(const int*, const double&); // OK, specialization of g1<int, const double> — end example]
An abbreviated function template can have a template-head.
The invented template-parameters are appended to the template-parameter-list after the explicitly declared template-parameters.
[Example 10: template<typename> concept C = /* ... */; template <typename T, C U> void g(T x, U y, C auto z);
This is functionally equivalent to each of the following two declarations.
template<typename T, C U, C W> void g(T x, U y, W z); template<typename T, typename U, typename W> requires C<U> && C<W> void g(T x, U y, W z); — end example]
A function declaration at block scope shall not declare an abbreviated function template.
A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration.
When it is part of a parameter-declaration-clause, the parameter-declaration declares a function parameter pack ([temp.variadic]).
Otherwise, the parameter-declaration is part of a template-parameter-list and declares a template parameter pack; see [temp.param].
A function parameter pack is a pack expansion ([temp.variadic]).
[Example 11: template<typename... T> void f(T (* ...t)(int, int)); int add(int, int); float subtract(int, int); void g() { f(add, subtract); } — end example]
There is a syntactic ambiguity when an ellipsis occurs at the end of a parameter-declaration-clause without a preceding comma.
In this case, the ellipsis is parsed as part of the abstract-declarator if the type of the parameter either names a template parameter pack that has not been expanded or contains auto; otherwise, it is parsed as part of the parameter-declaration-clause.77
76)76)
As indicated by syntax, cv-qualifiers are a significant component in function return types.
77)77)
One can explicitly disambiguate the parse either by introducing a comma (so the ellipsis will be parsed as part of the parameter-declaration-clause) or by introducing a name for the parameter (so the ellipsis will be parsed as part of the declarator-id).

9.3.4.7 Default arguments [dcl.fct.default]

If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument.
[Note 1: 
Default arguments will be used in calls where trailing arguments are missing ([expr.call]).
— end note]
[Example 1: 
The declaration void point(int = 3, int = 4); declares a function that can be called with zero, one, or two arguments of type int.
It can be called in any of these ways: point(1,2); point(1); point();
The last two calls are equivalent to point(1,4) and point(3,4), respectively.
— end example]
A default argument shall be specified only in the parameter-declaration-clause of a function declaration or lambda-declarator or in a template-parameter ([temp.param]).
A default argument shall not be specified for a template parameter pack or a function parameter pack.
If it is specified in a parameter-declaration-clause, it shall not occur within a declarator or abstract-declarator of a parameter-declaration.78
For non-template functions, default arguments can be added in later declarations of a function that inhabit the same scope.
Declarations that inhabit different scopes have completely distinct sets of default arguments.
That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.
In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration, unless the parameter was expanded from a parameter pack, or shall be a function parameter pack.
[Note 2: 
A default argument cannot be redefined by a later declaration (not even to the same value) ([basic.def.odr]).
— end note]
[Example 2: void g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow // a parameter with a default argument void f(int, int); void f(int, int = 7); void h() { f(3); // OK, calls f(3, 7) void f(int = 1, int); // error: does not use default from surrounding scope } void m() { void f(int, int); // has no defaults f(4); // error: wrong number of arguments void f(int, int = 5); // OK f(4); // OK, calls f(4, 5); void f(int, int = 5); // error: cannot redefine, even to same value } void n() { f(6); // OK, calls f(6, 7) } template<class ... T> struct C { void f(int n = 0, T...); }; C<int> c; // OK, instantiates declaration void C​::​f(int n = 0, int) — end example]
For a given inline function defined in different translation units, the accumulated sets of default arguments at the end of the translation units shall be the same; no diagnostic is required.
If a friend declaration D specifies a default argument expression, that declaration shall be a definition and there shall be no other declaration of the function or function template which is reachable from D or from which D is reachable.
The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics ([dcl.init]).
The names in the default argument are looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation ([expr.const]) that is a potentially-evaluated subexpression ([intro.execution]) of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point.
Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in [temp.inst].
[Example 3: 
In the following code, g will be called with the value f(2): int a = 1; int f(int); int g(int x = f(a)); // default argument: f(​::​a) void h() { a = 2; { int a = 3; g(); // g(f(​::​a)) } }
— end example]
[Note 3: 
A default argument is a complete-class context ([class.mem]).
Access checking applies to names in default arguments as described in [class.access].
— end note]
Except for member functions of templated classes, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition; the program is ill-formed if a default constructor ([class.default.ctor]), copy or move constructor ([class.copy.ctor]), or copy or move assignment operator ([class.copy.assign]) is so declared.
Default arguments for a member function of a templated class shall be specified on the initial declaration of the member function within the templated class.
[Example 4: class C { void f(int i = 3); void g(int i, int j = 99); }; void C::f(int i = 3) {} // error: default argument already specified in class scope void C::g(int i = 88, int j) {} // in this translation unit, C​::​g can be called with no arguments — end example]
[Note 4: 
A local variable cannot be odr-used ([basic.def.odr]) in a default argument.
— end note]
[Example 5: void f() { int i; extern void g(int x = i); // error extern void h(int x = sizeof(i)); // OK // ... } — end example]
[Note 5: 
The keyword this cannot appear in a default argument of a member function; see [expr.prim.this].
[Example 6: class A { void f(A* p = this) { } // error }; — end example]
— end note]
A default argument is evaluated each time the function is called with no argument for the corresponding parameter.
A parameter shall not appear as a potentially-evaluated expression in a default argument.
[Note 6: 
Parameters of a function declared before a default argument are in scope and can hide namespace and class member names.
— end note]
[Example 7: int a; int f(int a, int b = a); // error: parameter a used as default argument typedef int I; int g(float I, int b = I(2)); // error: parameter I found int h(int a, int b = sizeof(a)); // OK, unevaluated operand — end example]
A non-static member shall not appear in a default argument unless it appears as the id-expression of a class member access expression ([expr.ref]) or unless it is used to form a pointer to member ([expr.unary.op]).
[Example 8: 
The declaration of X​::​mem1() in the following example is ill-formed because no object is supplied for the non-static member X​::​a used as an initializer.
int b; class X { int a; int mem1(int i = a); // error: non-static member a used as default argument int mem2(int i = b); // OK; use X​::​b static int b; };
The declaration of X​::​mem2() is meaningful, however, since no object is needed to access the static member X​::​b.
Classes, objects, and members are described in [class].
— end example]
A default argument is not part of the type of a function.
[Example 9: int f(int = 0); void h() { int j = f(1); int k = f(); // OK, means f(0) } int (*p1)(int) = &f; int (*p2)() = &f; // error: type mismatch — end example]
When an overload set contains a declaration of a function that inhabits a scope S, any default argument associated with any reachable declaration that inhabits S is available to the call.
[Note 7: 
The candidate might have been found through a using-declarator from which the declaration that provides the default argument is not reachable.
— end note]
A virtual function call ([class.virtual]) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object.
An overriding function in a derived class does not acquire default arguments from the function it overrides.
[Example 10: struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); // OK, calls pa->B​::​f(7) pb->f(); // error: wrong number of arguments for B​::​f() } — end example]
78)78)
This means that default arguments cannot appear, for example, in declarations of pointers to functions, references to functions, or typedef declarations.

9.4 Initializers [dcl.init]

9.4.1 General [dcl.init.general]

Except for objects declared with the constexpr specifier, for which see [dcl.constexpr], an initializer in the definition of a variable can consist of arbitrary expressions involving literals and previously declared variables and functions, regardless of the variable's storage duration.
[Example 1: int f(int); int a = 2; int b = f(a); int c(b); — end example]
[Note 2: 
Default arguments are more restricted; see [dcl.fct.default].
— end note]
[Note 3: 
The order of initialization of variables with static storage duration is described in [basic.start] and [stmt.dcl].
— end note]
A declaration D of a variable with linkage shall not have an initializer if D inhabits a block scope.
To zero-initialize an object or reference of type T means:
  • if T is a scalar type ([basic.types.general]), the object is initialized to the value obtained by converting the integer literal 0 (zero) to T;79
  • if T is a (possibly cv-qualified) non-union class type, its padding bits ([basic.types.general]) are initialized to zero bits and each non-static data member, each non-virtual base class subobject, and, if the object is not a base class subobject, each virtual base class subobject is zero-initialized;
  • if T is a (possibly cv-qualified) union type, its padding bits ([basic.types.general]) are initialized to zero bits and the object's first non-static named data member is zero-initialized;
  • if T is an array type, each element is zero-initialized;
  • if T is a reference type, no initialization is performed.
To default-initialize an object of type T means:
  • If T is a (possibly cv-qualified) class type ([class]), constructors are considered.
    The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution ([over.match]).
    The constructor thus selected is called, with an empty argument list, to initialize the object.
  • If T is an array type, the semantic constraints of default-initializing a hypothetical element shall be met and each element is default-initialized.
  • Otherwise, no initialization is performed.
A class type T is const-default-constructible if default-initialization of T would invoke a user-provided constructor of T (not inherited from a base class) or if
  • each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
  • if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
  • if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data member has a default member initializer, and
  • each potentially constructed base class of T is const-default-constructible.
If a program calls for the default-initialization of an object of a const-qualified type T, T shall be a const-default-constructible class type or array thereof.
To value-initialize an object of type T means:
  • If T is a (possibly cv-qualified) class type ([class]), then let C be the constructor selected to default-initialize the object, if any.
    If C is not user-provided, the object is first zero-initialized.
    In all cases, the object is then default-initialized.
  • If T is an array type, the semantic constraints of value-initializing a hypothetical element shall be met and each element is value-initialized.
  • Otherwise, the object is zero-initialized.
A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed.
[Note 4: 
For every object of static storage duration, static initialization ([basic.start.static]) is performed at program startup before any other initialization takes place.
In some cases, additional initialization is done later.
— end note]
If no initializer is specified for an object, the object is default-initialized.
If the entity being initialized does not have class or array type, the expression-list in a parenthesized initializer shall be a single expression.
The initialization that occurs in the = form of a brace-or-equal-initializer or condition ([stmt.select]), as well as in argument passing, function return, throwing an exception ([except.throw]), handling an exception ([except.handle]), and aggregate member initialization other than by a designated-initializer-clause ([dcl.init.aggr]), is called copy-initialization.
[Note 5: 
Copy-initialization can invoke a move ([class.copy.ctor]).
— end note]
The initialization that occurs is called direct-initialization.
The semantics of initializers are as follows.
The destination type is the cv-unqualified type of the object or reference being initialized and the source type is the type of the initializer expression.
If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.
  • If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized ([dcl.init.list]).
  • If the destination type is a reference type, see [dcl.init.ref].
  • If the destination type is an array of characters, an array of char8_t, an array of char16_t, an array of char32_t, or an array of wchar_t, and the initializer is a string-literal, see [dcl.init.string].
  • If the initializer is (), the object is value-initialized.
    [Note 6: 
    Since () is not permitted by the syntax for initializer, X a(); is not the declaration of an object of class X, but the declaration of a function taking no arguments and returning an X.
    The form () can appear in certain other initialization contexts ([expr.new], [expr.type.conv], [class.base.init]).
    — end note]
  • Otherwise, if the destination type is an array, the object is initialized as follows.
    The initializer shall be of the form ( expression-list ).
    Let , , be the elements of the expression-list.
    If the destination type is an array of unknown bound, it is defined as having k elements.
    Let n denote the array size after this potential adjustment.
    If k is greater than n, the program is ill-formed.
    Otherwise, the array element is copy-initialized with for each 1  ≤ i  ≤ k, and value-initialized for each .
    For each , every value computation and side effect associated with the initialization of the element of the array is sequenced before those associated with the initialization of the element.
  • Otherwise, if the destination type is a class type:
    • If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same as the destination type, the initializer expression is used to initialize the destination object.
      [Example 2: 
      T x = T(T(T())); value-initializes x.
      — end example]
    • Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same as or is derived from the class of the destination type, constructors are considered.
      The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution ([over.match]).
      Then:
      • If overload resolution is successful, the selected constructor is called to initialize the object, with the initializer expression or expression-list as its argument(s).
      • Otherwise, if no constructor is viable, the destination type is an aggregate class, and the initializer is a parenthesized expression-list, the object is initialized as follows.
        Let , , be the elements of the aggregate ([dcl.init.aggr]).
        Let , , be the elements of the expression-list.
        If k is greater than n, the program is ill-formed.
        The element is copy-initialized with for 1  ≤ i  ≤ k.
        The remaining elements are initialized with their default member initializers, if any, and otherwise are value-initialized.
        For each , every value computation and side effect associated with the initialization of is sequenced before those associated with the initialization of .
        [Note 7: 
        By contrast with direct-list-initialization, narrowing conversions ([dcl.init.list]) can appear, designators are not permitted, a temporary object bound to a reference does not have its lifetime extended ([class.temporary]), and there is no brace elision.
        [Example 3: struct A { int a; int&& r; }; int f(); int n = 10; A a1{1, f()}; // OK, lifetime is extended A a2(1, f()); // well-formed, but dangling reference A a3{1.0, 1}; // error: narrowing conversion A a4(1.0, 1); // well-formed, but dangling reference A a5(1.0, std::move(n)); // OK — end example]
        — end note]
      • Otherwise, the initialization is ill-formed.
    • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversions that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution ([over.match]).
      If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
      The function selected is called with the initializer expression as its argument; if the function is a constructor, the call is a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor.
      The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.
  • Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered.
    The applicable conversion functions are enumerated ([over.match.conv]), and the best one is chosen through overload resolution ([over.match]).
    The user-defined conversion so selected is called to convert the initializer expression into the object being initialized.
    If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
  • Otherwise, if the initialization is direct-initialization, the source type is std​::​nullptr_t, and the destination type is bool, the initial value of the object being initialized is false.
  • Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression.
    A standard conversion sequence ([conv]) is used to convert the initializer expression to a prvalue of the destination type; no user-defined conversions are considered.
    If the conversion cannot be done, the initialization is ill-formed.
    When initializing a bit-field with a value that it cannot represent, the resulting value of the bit-field is implementation-defined.
    [Note 8: 
    An expression of type “cv1 T” can initialize an object of type “cv2 T” independently of the cv-qualifiers cv1 and cv2.
    int a; const int b = a; int c = b; — end note]
An immediate invocation ([expr.const]) that is not evaluated where it appears ([dcl.fct.default], [class.mem.general]) is evaluated and checked for whether it is a constant expression at the point where the enclosing initializer is used in a function call, a constructor definition, or an aggregate initialization.
An initializer-clause followed by an ellipsis is a pack expansion ([temp.variadic]).
Initialization includes the evaluation of all subexpressions of each initializer-clause of the initializer (possibly nested within braced-init-lists) and the creation of any temporary objects for function arguments or return values ([class.temporary]).
If the initializer is a parenthesized expression-list, the expressions are evaluated in the order specified for function calls ([expr.call]).
The same identifier shall not appear in multiple designators of a designated-initializer-list.
An object whose initialization has completed is deemed to be constructed, even if the object is of non-class type or no constructor of the object's class is invoked for the initialization.
[Note 9: 
Such an object might have been value-initialized or initialized by aggregate initialization ([dcl.init.aggr]) or by an inherited constructor ([class.inhctor.init]).
— end note]
Destroying an object of class type invokes the destructor of the class.
Destroying a scalar type has no effect other than ending the lifetime of the object ([basic.life]).
Destroying an array destroys each element in reverse subscript order.
A declaration that specifies the initialization of a variable, whether from an explicit initializer or by default-initialization, is called the initializing declaration of that variable.
[Note 10: 
In most cases this is the defining declaration ([basic.def]) of the variable, but the initializing declaration of a non-inline static data member ([class.static.data]) can be the declaration within the class definition and not the definition (if any) outside it.
— end note]
79)79)
As specified in [conv.ptr], converting an integer literal whose value is 0 to a pointer type results in a null pointer value.

9.4.2 Aggregates [dcl.init.aggr]

An aggregate is an array or a class ([class]) with
[Note 1: 
Aggregate initialization does not allow accessing protected and private base class' members or constructors.
— end note]
The elements of an aggregate are:
  • for an array, the array elements in increasing subscript order, or
  • for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not members of an anonymous union, in declaration order.
When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate.
The explicitly initialized elements of the aggregate are determined as follows:
  • If the initializer list is a brace-enclosed designated-initializer-list, the aggregate shall be of class type, the identifier in each designator shall name a direct non-static data member of the class, and the explicitly initialized elements of the aggregate are the elements that are, or contain, those members.
  • If the initializer list is a brace-enclosed initializer-list, the explicitly initialized elements of the aggregate are those for which an element of the initializer list appertains to the aggregate element or to a subobject thereof (see below).
  • Otherwise, the initializer list must be {}, and there are no explicitly initialized elements.
For each explicitly initialized element:
  • If the element is an anonymous union member and the initializer list is a brace-enclosed designated-initializer-list, the element is initialized by the braced-init-list { D }, where D is the designated-initializer-clause naming a member of the anonymous union member.
    There shall be only one such designated-initializer-clause.
    [Example 1: 
    struct C { union { int a; const char* p; }; int x; } c = { .a = 1, .x = 3 }; initializes c.a with 1 and c.x with 3.
    — end example]
  • Otherwise, if the initializer list is a brace-enclosed designated-initializer-list, the element is initialized with the brace-or-equal-initializer of the corresponding designated-initializer-clause.
    If that initializer is of the form = assignment-expression and a narrowing conversion ([dcl.init.list]) is required to convert the expression, the program is ill-formed.
    [Note 2: 
    The form of the initializer determines whether copy-initialization or direct-initialization is performed.
    — end note]
  • Otherwise, the initializer list is a brace-enclosed initializer-list.
    If an initializer-clause appertains to the aggregate element, then the aggregate element is copy-initialized from the initializer-clause.
    Otherwise, the aggregate element is copy-initialized from a brace-enclosed initializer-list consisting of all of the initializer-clauses that appertain to subobjects of the aggregate element, in the order of appearance.
    [Note 3: 
    If an initializer is itself an initializer list, the element is list-initialized, which will result in a recursive application of the rules in this subclause if the element is an aggregate.
    — end note]
    [Example 2: 
    struct A { int x; struct B { int i; int j; } b; } a = { 1, { 2, 3 } }; initializes a.x with 1, a.b.i with 2, a.b.j with 3.
    struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; derived d2{{}, {}, 4}; initializes d1.b1 with 1, d1.b2 with 2, d1.b3 with 42, d1.d with 4, and d2.b1 with 0, d2.b2 with 42, d2.b3 with 42, d2.d with 4.
    — end example]
For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:
  • If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
  • Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
  • Otherwise, the program is ill-formed.
If the aggregate is a union and the initializer list is empty, then
  • if any variant member has a default member initializer, that member is initialized from its default member initializer;
  • otherwise, the first member of the union (if any) is copy-initialized from an empty initializer list.
[Example 3: 
struct S { int a; const char* b; int c; int d = b[a]; }; S ss = { 1, "asdf" }; initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an expression of the form int{} (that is, 0), and ss.d with the value of ss.b[ss.a] (that is, 's').
struct A { string a; int b = 42; int c = -1; };
A{.c=21} has the following steps:
  • Initialize a with {}
  • Initialize b with = 42
  • Initialize c with = 21
— end example]
The initializations of the elements of the aggregate are evaluated in the element order.
That is, all value computations and side effects associated with a given element are sequenced before those of any element that follows it in order.
An aggregate that is a class can also be initialized with a single expression not enclosed in braces, as described in [dcl.init].
The destructor for each element of class type other than an anonymous union member is potentially invoked ([class.dtor]) from the context where the aggregate initialization occurs.
[Note 4: 
This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown ([except.ctor]).
— end note]
The number of elements ([dcl.array]) in an array of unknown bound initialized with a brace-enclosed initializer-list is the number of explicitly initialized elements of the array.
[Example 4: 
int x[] = { 1, 3, 5 }; declares and initializes x as a one-dimensional array that has three elements since no size was specified and there are three initializers.
— end example]
[Example 5: 
In struct X { int i, j, k; }; X a[] = { 1, 2, 3, 4, 5, 6 }; X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } }; a and b have the same value.
— end example]
An array of unknown bound shall not be initialized with an empty braced-init-list {}.80
[Note 5: 
A default member initializer does not determine the bound for a member array of unknown bound.
Since the default member initializer is ignored if a suitable mem-initializer is present ([class.base.init]), the default member initializer is not considered to initialize the array of unknown bound.
[Example 6: struct S { int y[] = { 0 }; // error: non-static data member of incomplete type }; — end example]
— end note]
[Note 6: 
Static data members, non-static data members of anonymous union members, and unnamed bit-fields are not considered elements of the aggregate.
[Example 7: struct A { int i; static int s; int j; int :17; int k; } a = { 1, 2, 3 };
Here, the second initializer 2 initializes a.j and not the static data member A​::​s, and the third initializer 3 initializes a.k and not the unnamed bit-field before it.
— end example]
— end note]
If a member has a default member initializer and a potentially-evaluated subexpression thereof is an aggregate initialization that would use that default member initializer, the program is ill-formed.
[Example 8: struct A; extern A a; struct A { const A& a1 { A{a,a} }; // OK const A& a2 { A{} }; // error }; A a{a,a}; // OK struct B { int n = B{}.n; // error }; — end example]
When initializing a multidimensional array, the initializer-clauses initialize the elements with the last (rightmost) index of the array varying the fastest ([dcl.array]).
[Example 9: 
int x[2][2] = { 3, 1, 4, 2 }; initializes x[0][0] to 3, x[0][1] to 1, x[1][0] to 4, and x[1][1] to 2.
On the other hand, float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; initializes the first column of y (regarded as a two-dimensional array) and leaves the rest zero.
— end example]
Each initializer-clause in a brace-enclosed initializer-list is said to appertain to an element of the aggregate being initialized or to an element of one of its subaggregates.
Considering the sequence of initializer-clauses, and the sequence of aggregate elements initially formed as the sequence of elements of the aggregate being initialized and potentially modified as described below, each initializer-clause appertains to the corresponding aggregate element if
  • the aggregate element is not an aggregate, or
  • the initializer-clause begins with a left brace, or
  • the initializer-clause is an expression and an implicit conversion sequence can be formed that converts the expression to the type of the aggregate element, or
  • the aggregate element is an aggregate that itself has no aggregate elements.
Otherwise, the aggregate element is an aggregate and that subaggregate is replaced in the list of aggregate elements by the sequence of its own aggregate elements, and the appertainment analysis resumes with the first such element and the same initializer-clause.
[Note 7: 
These rules apply recursively to the aggregate's subaggregates.
[Example 10: 
In struct S1 { int a, b; }; struct S2 { S1 s, t; }; S2 x[2] = { 1, 2, 3, 4, 5, 6, 7, 8 }; S2 y[2] = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }; x and y have the same value.
— end example]
— end note]
This process continues until all initializer-clauses have been exhausted.
If any initializer-clause remains that does not appertain to an element of the aggregate or one of its subaggregates, the program is ill-formed.
[Example 11: char cv[4] = { 'a', 's', 'd', 'f', 0 }; // error: too many initializers — end example]
[Example 12: 
float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; is a completely-braced initialization: 1, 3, and 5 initialize the first row of the array y[0], namely y[0][0], y[0][1], and y[0][2].
Likewise the next two lines initialize y[1] and y[2].
The initializer ends early and therefore y[3]'s elements are initialized as if explicitly initialized with an expression of the form float(), that is, are initialized with 0.0.
In the following example, braces in the initializer-list are elided; however the initializer-list has the same effect as the completely-braced initializer-list of the above example, float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
The initializer for y begins with a left brace, but the one for y[0] does not, therefore three elements from the list are used.
Likewise the next three are taken successively for y[1] and y[2].
— end example]
[Note 8: 
The initializer for an empty subaggregate is needed if any initializers are provided for subsequent elements.
[Example 13: struct S { } s; struct A { S s1; int i1; S s2; int i2; S s3; int i3; } a = { { }, // Required initialization 0, s, // Required initialization 0 }; // Initialization not required for A​::​s3 because A​::​i3 is also not initialized — end example]
— end note]
[Example 14: struct A { int i; operator int(); }; struct B { A a1, a2; int z; }; A a; B b = { 4, a, a };
Braces are elided around the initializer-clause for b.a1.i.
b.a1.i is initialized with 4, b.a2 is initialized with a, b.z is initialized with whatever a.operator int() returns.
— end example]
[Note 9: 
An aggregate array or an aggregate class can contain elements of a class type with a user-declared constructor ([class.ctor]).
Initialization of these aggregate objects is described in [class.expl.init].
— end note]
[Note 10: 
Whether the initialization of aggregates with static storage duration is static or dynamic is specified in [basic.start.static], [basic.start.dynamic], and [stmt.dcl].
— end note]
When a union is initialized with an initializer list, there shall not be more than one explicitly initialized element.
[Example 15: union u { int a; const char* b; }; u a = { 1 }; u b = a; u c = 1; // error u d = { 0, "asdf" }; // error u e = { "asdf" }; // error u f = { .b = "asdf" }; u g = { .a = 1, .b = "asdf" }; // error — end example]
[Note 11: 
As described above, the braces around the initializer-clause for a union member can be omitted if the union is a member of another aggregate.
— end note]
80)80)
The syntax provides for empty braced-init-lists, but nonetheless C++ does not have zero length arrays.

9.4.3 Character arrays [dcl.init.string]

An array of ordinary character type ([basic.fundamental]), char8_t array, char16_t array, char32_t array, or wchar_t array may be initialized by an ordinary string literal, UTF-8 string literal, UTF-16 string literal, UTF-32 string literal, or wide string literal, respectively, or by an appropriately-typed string-literal enclosed in braces ([lex.string]).
Additionally, an array of char or unsigned char may be initialized by a UTF-8 string literal, or by such a string literal enclosed in braces.
Successive characters of the value of the string-literal initialize the elements of the array, with an integral conversion ([conv.integral]) if necessary for the source and destination value.
[Example 1: 
char msg[] = "Syntax error on line %s\n"; shows a character array whose members are initialized with a string-literal.
Note that because '\n' is a single character and because a trailing '\0' is appended, sizeof(msg) is 25.
— end example]
There shall not be more initializers than there are array elements.
[Example 2: 
char cv[4] = "asdf"; // error is ill-formed since there is no space for the implied trailing '\0'.
— end example]
If there are fewer initializers than there are array elements, each element not explicitly initialized shall be zero-initialized ([dcl.init]).

9.4.4 References [dcl.init.ref]

A variable whose declared type is “reference to T” ([dcl.ref]) shall be initialized.
[Example 1: int g(int) noexcept; void f() { int i; int& r = i; // r refers to i r = 1; // the value of i becomes 1 int* p = &r; // p points to i int& rr = r; // rr refers to what r refers to, that is, to i int (&rg)(int) = g; // rg refers to the function g rg(i); // calls function g int a[3]; int (&ra)[3] = a; // ra refers to the array a ra[1] = i; // modifies a[1] } — end example]
A reference cannot be changed to refer to another object after initialization.
[Note 1: 
Assignment to a reference assigns to the object referred to by the reference ([expr.ass]).
— end note]
Argument passing ([expr.call]) and function value return ([stmt.return]) are initializations.
The initializer can be omitted for a reference only in a parameter declaration ([dcl.fct]), in the declaration of a function return type, in the declaration of a class member within its class definition ([class.mem]), and where the extern specifier is explicitly used.
[Example 2: int& r1; // error: initializer missing extern int& r2; // OK — end example]
Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is similar ([conv.qual]) to T2, or T1 is a base class of T2.
cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence ([conv]).
In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
  • If the reference is an lvalue reference and the initializer expression
    • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, or
    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T381 (this conversion is selected by enumerating the applicable conversion functions ([over.match.ref]) and choosing the best one through overload resolution),
    then the reference binds to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
    [Note 2: 
    The usual lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done.
    — end note]
    [Example 3: double d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A { }; struct B : A { operator int&(); } b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b int& ir = B(); // ir refers to the result of B​::​operator int& — end example]
  • Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.
    [Example 4: double& rd2 = 2.0; // error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const — end example]
  • Otherwise, if the initializer expression
    • is an rvalue (but not a bit-field) or an lvalue of function type and “cv1 T1” is reference-compatible with “cv2 T2”, or
    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue of type “cv3 T3” or an lvalue of function type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref]),
    then the initializer expression in the first case and the converted expression in the second case is called the converted initializer.
    If the converted initializer is a prvalue, let its type be denoted by T4; the temporary materialization conversion ([conv.rval]) is applied, considering the type of the prvalue to be “cv1 T4” ([conv.qual]).
    In any case, the reference binds to the resulting glvalue (or to an appropriate base class subobject).
    [Example 5: struct A { }; struct B : A { } b; extern B f(); const A& rca2 = f(); // binds to the A subobject of the B rvalue. A&& rra = f(); // same as above struct X { operator B(); operator int&(); } x; const A& r = x; // binds to the A subobject of the result of the conversion int i2 = 42; int&& rri = static_cast<int&&>(i2); // binds directly to i2 B&& rrb = x; // binds directly to the result of operator B constexpr int f() { const int &x = 42; const_cast<int &>(x) = 1; // undefined behavior return x; } constexpr int z = f(); // error: not a constant expression — end example]
  • Otherwise, T1 shall not be reference-related to T2.
    • If T1 or T2 is a class type, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed.
      The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference.
      For this direct-initialization, user-defined conversions are not considered.
    • Otherwise, the initializer expression is implicitly converted to a prvalue of type “T1.
      The temporary materialization conversion is applied, considering the type of the prvalue to be “cv1 T1”, and the reference is bound to the result.
    [Example 6: struct Banana { }; struct Enigma { operator const Banana(); }; struct Alaska { operator Banana&(); }; void enigmatic() { typedef const Banana ConstBanana; Banana &&banana1 = ConstBanana(); // error Banana &&banana2 = Enigma(); // error Banana &&banana3 = Alaska(); // error } const double& rcd2 = 2; // rcd2 refers to temporary with type const double and value 2.0 double&& rrd = 2; // rrd refers to temporary with value 2.0 const volatile int cvi = 1; const int& r2 = cvi; // error: cv-qualifier dropped struct A { operator volatile int&(); } a; const int& r3 = a; // error: cv-qualifier dropped // from result of conversion function double d2 = 1.0; double&& rrd2 = d2; // error: initializer is lvalue of reference-related type struct X { operator int&(); }; int&& rri2 = X(); // error: result of conversion function is // lvalue of reference-related type int i3 = 2; double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0 — end example]
In all cases except the last (i.e., implicitly converting the initializer expression to the referenced type), the reference is said to bind directly to the initializer expression.
[Note 3: 
[class.temporary] describes the lifetime of temporaries bound to references.
— end note]
81)81)
This requires a conversion function ([class.conv.fct]) returning a reference type.

9.4.5 List-initialization [dcl.init.list]

List-initialization is initialization of an object or reference from a braced-init-list.
Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the initializer-list or designated-initializer-clauses of the designated-initializer-list are called the elements of the initializer list.
An initializer list may be empty.
List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.
Direct-initialization that is not list-initialization is called direct-non-list-initialization.
[Note 1: 
List-initialization can be used
[Example 1: int a = {1}; std::complex<double> z{1,2}; new std::vector<std::string>{"once", "upon", "a", "time"}; // 4 string elements f( {"Nicholas","Annemarie"} ); // pass list of two elements return { "Norah" }; // return list of one element int* e {}; // initialization to zero / null pointer x = double{1}; // explicitly construct a double std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} }; — end example]
— end note]
A constructor is an initializer-list constructor if its first parameter is of type std​::​initializer_list<E> or reference to cv std​::​initializer_list<E> for some type E, and either there are no other parameters or else all other parameters have default arguments ([dcl.fct.default]).
[Note 2: 
Initializer-list constructors are favored over other constructors in list-initialization ([over.match.list]).
Passing an initializer list as the argument to the constructor template template<class T> C(T) of a class C does not create an initializer-list constructor, because an initializer list argument causes the corresponding parameter to be a non-deduced context ([temp.deduct.call]).
— end note]
The template std​::​initializer_list is not predefined; if a standard library declaration ([initializer.list.syn], [std.modules]) of std​::​initializer_list is not reachable from ([module.reach]) a use of std​::​initializer_list — even an implicit use in which the type is not named ([dcl.spec.auto]) — the program is ill-formed.
List-initialization of an object or reference of type cv T is defined as follows:
  • If the braced-init-list contains a designated-initializer-list and T is not a reference type, T shall be an aggregate class.
    The ordered identifiers in the designators of the designated-initializer-list shall form a subsequence of the ordered identifiers in the direct non-static data members of T.
    Aggregate initialization is performed ([dcl.init.aggr]).
    [Example 2: struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, b.y initialized to 0 — end example]
  • If T is an aggregate class and the initializer list has a single element of type cv1 U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
  • Otherwise, if T is a character array and the initializer list has a single element that is an appropriately-typed string-literal ([dcl.init.string]), initialization is performed as described in that subclause.
  • Otherwise, if T is an aggregate, aggregate initialization is performed ([dcl.init.aggr]).
    [Example 3: double ad[] = { 1, 2.0 }; // OK int ai[] = { 1, 2.0 }; // error: narrowing struct S2 { int m1; double m2, m3; }; S2 s21 = { 1, 2, 3.0 }; // OK S2 s22 { 1.0, 2, 3 }; // error: narrowing S2 s23 { }; // OK, default to 0,0,0 — end example]
  • Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
  • Otherwise, if T is a specialization of std​::​initializer_list, the object is constructed as described below.
  • Otherwise, if T is a class type, constructors are considered.
    The applicable constructors are enumerated and the best one is chosen through overload resolution ([over.match], [over.match.list]).
    If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
    [Example 4: struct S { S(std::initializer_list<double>); // #1 S(std::initializer_list<int>); // #2 S(std::initializer_list<S>); // #3 S(); // #4 // ... }; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2 S s3{s2}; // invoke #3 (not the copy constructor) S s4 = { }; // invoke #4 — end example]
    [Example 5: struct Map { Map(std::initializer_list<std::pair<std::string,int>>); }; Map ship = {{"Sophie",14}, {"Surprise",28}}; — end example]
    [Example 6: struct S { // no initializer-list constructors S(int, double, double); // #1 S(); // #2 // ... }; S s1 = { 1, 2, 3.0 }; // OK, invoke #1 S s2 { 1.0, 2, 3 }; // error: narrowing S s3 { }; // OK, invoke #2 — end example]
  • Otherwise, if T is an enumeration with a fixed underlying type ([dcl.enum]) U, the initializer-list has a single element v of scalar type, v can be implicitly converted to U, and the initialization is direct-list-initialization, the object is initialized with the value T(v) ([expr.type.conv]); if a narrowing conversion is required to convert v to U, the program is ill-formed.
    [Example 7: enum byte : unsigned char { }; byte b { 42 }; // OK byte c = { 42 }; // error byte d = byte{ 42 }; // OK; same value as b byte e { -1 }; // error struct A { byte b; }; A a1 = { { 42 } }; // error A a2 = { byte{ 42 } }; // OK void f(byte); f({ 42 }); // error enum class Handle : uint32_t { Invalid = 0 }; Handle h { 42 }; // OK — end example]
  • Otherwise, if the initializer list is not a designated-initializer-list and has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization); if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
    [Example 8: int x1 {2}; // OK int x2 {2.0}; // error: narrowing — end example]
  • Otherwise, if T is a reference type, a prvalue is generated.
    The prvalue initializes its result object by copy-list-initialization from the initializer list.
    The prvalue is then used to direct-initialize the reference.
    The type of the prvalue is the type referenced by T, unless T is “reference to array of unknown bound of U”, in which case the type of the prvalue is the type of x in the declaration U x[] H, where H is the initializer list.
    [Note 3: 
    As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type.
    — end note]
    [Example 9: struct S { S(std::initializer_list<double>); // #1 S(const std::string&); // #2 // ... }; const S& r1 = { 1, 2, 3.0 }; // OK, invoke #1 const S& r2 { "Spinach" }; // OK, invoke #2 S& r3 = { 1, 2, 3 }; // error: initializer is not an lvalue const int& i1 = { 1 }; // OK const int& i2 = { 1.1 }; // error: narrowing const int (&iar)[2] = { 1, 2 }; // OK, iar is bound to temporary array struct A { } a; struct B { explicit B(const A&); }; const B& b2{a}; // error: cannot copy-list-initialize B temporary from A struct C { int x; }; C&& c = { .x = 1 }; // OK — end example]
  • Otherwise, if the initializer list has no elements, the object is value-initialized.
    [Example 10: int** pp {}; // initialized to null pointer — end example]
  • Otherwise, the program is ill-formed.
    [Example 11: struct A { int i; int j; }; A a1 { 1, 2 }; // aggregate initialization A a2 { 1.2 }; // error: narrowing struct B { B(std::initializer_list<int>); }; B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor B b2 { 1, 2.0 }; // error: narrowing struct C { C(int i, double j); }; C c1 = { 1, 2.2 }; // calls constructor with arguments (1, 2.2) C c2 = { 1.1, 2 }; // error: narrowing int j { 1 }; // initialize to 1 int k { }; // initialize to 0 — end example]
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear.
That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
[Note 4: 
This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call.
— end note]
An object of type std​::​initializer_list<E> is constructed from an initializer list as if the implementation generated and materialized ([conv.rval]) a prvalue of type “array of N const E”, where N is the number of elements in the initializer list; this is called the initializer list's backing array.
Each element of the backing array is copy-initialized with the corresponding element of the initializer list, and the std​::​initializer_list<E> object is constructed to refer to that array.
[Note 5: 
A constructor or conversion function selected for the copy needs to be accessible ([class.access]) in the context of the initializer list.
— end note]
If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.
[Note 6: 
Backing arrays are potentially non-unique objects ([intro.object]).
— end note]
The backing array has the same lifetime as any other temporary object ([class.temporary]), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
[Example 12: void f(std::initializer_list<double> il); void g(float x) { f({1, x, 3}); } void h() { f({1, 2, 3}); } struct A { mutable int i; }; void q(std::initializer_list<A>); void r() { q({A{1}, A{2}, A{3}}); }
The initialization will be implemented in a way roughly equivalent to this: void g(float x) { const double __a[3] = {double{1}, double{x}, double{3}}; // backing array f(std::initializer_list<double>(__a, __a+3)); } void h() { static constexpr double __b[3] = {double{1}, double{2}, double{3}}; // backing array f(std::initializer_list<double>(__b, __b+3)); } void r() { const A __c[3] = {A{1}, A{2}, A{3}}; // backing array q(std::initializer_list<A>(__c, __c+3)); } assuming that the implementation can construct an initializer_list object with a pair of pointers, and with the understanding that __b does not outlive the call to f.
— end example]
[Example 13: typedef std::complex<double> cmplx; std::vector<cmplx> v1 = { 1, 2, 3 }; void f() { std::vector<cmplx> v2{ 1, 2, 3 }; std::initializer_list<int> i3 = { 1, 2, 3 }; } struct A { std::initializer_list<int> i4; A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference };
For v1 and v2, the initializer_list object is a parameter in a function call, so the array created for { 1, 2, 3 } has full-expression lifetime.
For i3, the initializer_list object is a variable, so the array persists for the lifetime of the variable.
For i4, the initializer_list object is initialized in the constructor's ctor-initializer as if by binding a temporary array to a reference member, so the program is ill-formed ([class.base.init]).
— end example]
A narrowing conversion is an implicit conversion
  • from a floating-point type to an integer type, or
  • from a floating-point type T to another floating-point type whose floating-point conversion rank is neither greater than nor equal to that of T, except where the result of the conversion is a constant expression and either its value is finite and the conversion did not overflow, or the values before and after the conversion are not finite, or
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where
    • the source is a bit-field whose width w is less than that of its type (or, for an enumeration type, its underlying type) and the target type can represent all the values of a hypothetical extended integer type with width w and with the same signedness as the original type or
    • the source is a constant expression whose value after integral promotions will fit into the target type, or
  • from a pointer type or a pointer-to-member type to bool.
[Note 7: 
As indicated above, such conversions are not allowed at the top level in list-initializations.
— end note]
[Example 14: int x = 999; // x is not a constant expression const int y = 999; const int z = 99; char c1 = x; // OK, though it potentially narrows (in this case, it does narrow) char c2{x}; // error: potentially narrows char c3{y}; // error: narrows (assuming char is 8 bits) char c4{z}; // OK, no narrowing needed unsigned char uc1 = {5}; // OK, no narrowing needed unsigned char uc2 = {-1}; // error: narrows unsigned int ui1 = {-1}; // error: narrows signed int si1 = { (unsigned int)-1 }; // error: narrows int ii = {2.0}; // error: narrows float f1 { x }; // error: potentially narrows float f2 { 7 }; // OK, 7 can be exactly represented as a float bool b = {"meow"}; // error: narrows int f(int); int a[] = { 2, f(2), f(2.0) }; // OK, the double-to-int conversion is not at the top level — end example]

9.5 Function definitions [dcl.fct.def]

9.5.1 General [dcl.fct.def.general]

deleted-function-body:
= delete ;
= delete ( unevaluated-string ) ;
Any informal reference to the body of a function should be interpreted as a reference to the non-terminal function-body, including, for a constructor, default member initializers or default initialization used to initialize a base or member subobject in the absence of a mem-initializer-id ([class.base.init]).
The optional attribute-specifier-seq in a function-definition appertains to the function.
A function-definition with a requires-clause shall define a templated function.
In a function-definition, either void declarator ; or declarator ; shall be a well-formed function declaration as described in [dcl.fct].
A function shall be defined only in namespace or class scope.
The type of a parameter or the return type for a function definition shall not be a (possibly cv-qualified) class type that is incomplete or abstract within the function body