[dcl.dcl] (original) (raw)

9 Declarations [dcl.dcl]

9.1 Preamble [dcl.pre]

Declarations generally specify how names are to be interpreted.

Declarations have the form

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 aninit-declarator-list, are described in [dcl.decl].

The attribute-specifier-seqappertains to each of the entities declared by the declarator_s_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 thedeclarator-id for that declaration.

— _end note_]

[Example 1: [[noreturn]] void f [[noreturn]] (); — _end example_]

Except where otherwise specified, the meaning of an attribute-declarationis implementation-defined.

A declaration occurs in a scope; the scope rules are summarized in [basic.lookup].

A declaration that declares a function or defines a class, namespace, template, or function also has one or more scopes nested within it.

These nested scopes, in turn, can have declarations nested within them.

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.

In these cases and whenever aclass-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (asclass-names, enum-names, orenumerators, depending on the syntax).

In such cases, the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.

[Example 2: enum { }; typedef class { }; — _end example_]

If the value of the expression when so converted is true, the declaration has no effect.

Otherwise, the program is ill-formed, and the resulting diagnostic message ([intro.compliance]) shall include the text of the string-literal, if one is supplied, except that characters not in the basic source character set are not required to appear in the diagnostic message.

[Example 3: static_assert(sizeof(int) == sizeof(void*), "wrong pointer size"); — _end example_]

An empty-declaration has no effect.

If the decl-specifier-seq contains any decl-specifier other thanstatic, thread_­local, auto ([dcl.spec.auto]), orcv-qualifiers, the program is ill-formed.

Each init-declarator in the init-declarator-listcontains exactly one declarator-id, which is the name declared by that init-declarator and hence one of the names declared by the declaration.

Thedefining-type-specifiers ([dcl.type]) in thedecl-specifier-seq and the recursive declaratorstructure of the init-declarator describe a type ([dcl.meaning]), which is then associated with the name being declared by the init-declarator.

If the decl-specifier-seq contains the typedefspecifier, the declaration is called a typedef declaration and the name of each init-declaratoris declared to be a typedef-name, synonymous with its associated type ([dcl.typedef]).

If thedecl-specifier-seq contains no typedef specifier, the declaration is called a function declaration if the type associated with the name is a function type ([dcl.fct]) and an object declaration otherwise.

Syntactic components beyond those found in the general form of declaration are added to a function declaration to make afunction-definition.

An object declaration, however, is also a definition unless it contains the extern specifier and has no initializer ([basic.def]).

An object definition causes storage of appropriate size and alignment to be reserved and any appropriate initialization ([dcl.init]) to be done.

A nodeclspec-function-declaration shall declare a constructor, destructor, or conversion function.

9.2 Specifiers [dcl.spec]

9.2.1 General [dcl.spec.general]

The specifiers that can be used in a declaration are

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

The optional attribute-specifier-seq in a decl-specifier-seqappertains to the type determined by the precedingdecl-specifiers ([dcl.meaning]).

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

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

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

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

The sequence shall be self-consistent as described below.

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

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

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

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

— _end example_]

[Note 1:

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

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

— _end note_]

9.2.2 Storage class specifiers [dcl.stc]

The storage class specifiers are

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

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

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

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

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

[Note 2:

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

— _end note_]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[Note 4:

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

— _end note_]

9.2.3 Function specifiers [dcl.fct.spec]

Afunction-specifiercan be used only in a function declaration.

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

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

Otherwise, the function is not explicit.

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

9.2.4 The typedef specifier [dcl.typedef]

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

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 thedecl-specifier-seq of aparameter-declaration ([dcl.fct]) nor in thedecl-specifier-seq of afunction-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 atypedef-name.

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

[Example 1:

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

— _end example_]

A typedef-name can also be introduced by analias-declaration.

The identifier following theusing keyword becomes a typedef-nameand the optional attribute-specifier-seq following theidentifier 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); using cell = pair<void*, cell*>; — _end example_]

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

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

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

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

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

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

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

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

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

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

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

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

[Note 2:

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

— _end note_]

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

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

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

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

9.2.6 The constexpr and consteval specifiers [dcl.constexpr]

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

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

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

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

[Note 1:

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

— _end note_]

[Note 2:

Function parameters cannot be declared constexpr.

— _end note_]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[Note 4:

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

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

— _end note_]

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

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

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

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

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

A constexpr variable shall have constant destruction.

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

9.2.7 The constinit specifier [dcl.constinit]

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

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

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

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

[Note 1:

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

— _end note_]

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

9.2.8 The inline specifier [dcl.inline]

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

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

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

[Note 1:

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

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

— _end note_]

A variable declaration with an inline specifier declares aninline variable.

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

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

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

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

[Note 2:

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

— _end note_]

[Note 3:

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

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

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

— _end note_]

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

[Note 4:

A constexpr function is implicitly inline.

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

— _end note_]

9.2.9 Type specifiers [dcl.type]

9.2.9.1 General [dcl.type.general]

The optional attribute-specifier-seq in a type-specifier-seqor a defining-type-specifier-seqappertains to the type denoted by the preceding type-specifier_s_or defining-type-specifiers ([dcl.meaning]).

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

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

The only exceptions to this rule are the following:

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

[Note 1:

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

— _end note_]

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

There are two cv-qualifiers, const andvolatile.

Each cv-qualifier shall appear at most once in a cv-qualifier-seq.

Redundant cv-qualifications are ignored.

[Note 2:

For example, these could be introduced by typedefs.

— _end note_]

[Note 3:

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

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

— _end note_]

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

[Note 4:

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

— _end note_]

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

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

— _end example_]

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

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

[Note 5:

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

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

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

— _end note_]

9.2.9.3 Simple type specifiers [dcl.type.simple]

The simple type specifiers are

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

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

[Note 1:

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

— _end note_]

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

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

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

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

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

[Note 2:

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

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

— _end note_]

9.2.9.4 Elaborated type specifiers [dcl.type.elab]

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

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

In the first case, the attribute-specifier-seq, if any, appertains to the class being declared; the attributes in theattribute-specifier-seq are thereafter considered attributes of the class whenever it is named.

[Note 2:

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

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

— _end note_]

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

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

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

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

9.2.9.5 Decltype specifiers [dcl.type.decltype]

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

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

[Note 1:

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

— _end note_]

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

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

[Note 2:

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

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

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

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

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

— _end note_]

[Note 3:

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

— _end note_]

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

9.2.9.6 Placeholder type specifiers [dcl.spec.auto]

9.2.9.6.1 General [dcl.spec.auto.general]

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

The placeholder type can appear with a function declarator in thedecl-specifier-seq, type-specifier-seq,conversion-function-id, or trailing-return-type, in any context where such a declarator is valid.

If the function declarator includes a trailing-return-type ([dcl.fct]), thattrailing-return-type specifies the declared return type of the function.

Otherwise, the function declarator shall declare a function.

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

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

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

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

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

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

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

If the init-declarator-list contains more than oneinit-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; auto a = 5, b = { 1, 2 }; — _end example_]

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

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

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

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

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

[Note 2:

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

— _end note_]

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

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

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

Return type deduction for a templated entity that is a function or function template with a placeholder in its declared type occurs when the definition is instantiated even if the function body contains a return statement with a non-type-dependent operand.

[Note 3:

Therefore, any use of a specialization of the function template will cause an implicit instantiation.

Any errors that arise from this instantiation are not in the immediate context of the function type and can result in the program being ill-formed ([temp.deduct]).

— _end note_]

[Example 5: template <class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } — _end example_]

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.

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

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

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

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

An explicit instantiation declaration does not cause the instantiation of an entity declared using a placeholder type, but it also does not prevent that entity from being instantiated as needed to determine its type.

[Example 7: template <typename T> auto f(T t) { return t; } extern template auto f(int); int (*p)(int) = f; — _end example_]

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

Placeholder type deductionis the process by which a type containing a placeholder type is replaced by a deduced type.

A type T containing a placeholder type, and a corresponding initializer E, are determined as follows:

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

If the deduction is for a return statement and E is a braced-init-list ([dcl.init.list]), the program is ill-formed.

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

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

If the deduction fails, the declaration is ill-formed.

Otherwise, is obtained by substituting the deduced U into P.

[Example 1: auto x1 = { 1, 2 }; auto x2 = { 1, 2.0 }; auto x3{ 1, 2 }; auto x4 = { 3 }; auto x5{ 3 }; — _end example_]

[Example 2: const auto &i = expr;

The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:template <class U> void f(const U& u);

— _end example_]

If the placeholder-type-specifier is of the formtype-constraint decltype(auto),T shall be the placeholder alone.

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

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

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

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

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

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

If the decl-specifier-seqis followed by an init-declarator-listor member-declarator-listcontaining more than one declarator, the type that replaces the placeholder shall be the same in each deduction.

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

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

9.3 Declarators [dcl.decl]

9.3.1 General [dcl.decl.general]

A declarator declares a single variable, function, or type, within a declaration.

Theinit-declarator-listappearing in a declaration is a comma-separated sequence of declarators, each of which can have an initializer.

The three components of asimple-declarationare the attributes ([dcl.attr]), the specifiers (decl-specifier-seq;[dcl.spec]) and the declarators (init-declarator-list).

The specifiers indicate the type, storage class or other properties of the entities being declared.

The declarators specify the names of these entities and (optionally) modify the type of the specifiers with operators such as*(pointer to) and()(function returning).

Initial values can also be specified in a declarator; initializers are discussed in [dcl.init] and [class.init].

Eachinit-declaratorin a declaration is analyzed separately as if it was in a declaration by itself.

[Note 1:

A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator.

That isT D1, D2, ... Dn;is usually equivalent toT D1; T D2; ... T Dn;where T is a decl-specifier-seqand each Di is an init-declarator.

One exception is when a name introduced by one of thedeclarators hides a type name used by thedecl-specifiers, so that when the samedecl-specifiers are used in a subsequent declaration, they do not have the same meaning, as instruct S { }; S S, T; which is not equivalent tostruct S { }; S S; S T;

Another exception is when T is auto ([dcl.spec.auto]), for example:auto i = 1, j = 2.0; as opposed toauto i = 1; auto j = 2.0;

— _end note_]

[Example 1: void f1(int a) requires true; template<typename T> auto f2(T a) -> bool requires true; template<typename T> auto f3(T a) requires true -> bool; void (*pf)() requires true; void g(int (*)() requires true); auto* p = new void(*)(char) requires true; — _end example_]

Declarators have the syntax

cv-qualifier:
const
volatile

9.3.2 Type names [dcl.name]

To specify type conversions explicitly,and as an argument ofsizeof,alignof,new, ortypeid, the name of a type shall be specified.

This can be done with atype-id, 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 theabstract-declaratorwhere 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 * int *[3] int (*)[3] int *() int (*)(double) name respectively the types “int”, “pointer toint”, “array of 3 pointers toint”, “pointer to array of 3int”, “function of (no parameters) returning pointer toint”, and “pointer to a function of (double) returningint”.

— _end example_]

A type can also be named (often more easily) by using atypedef ([dcl.typedef]).

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 a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer.

Just as for the ambiguities mentioned in [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration.

[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);};void foo(double a) { S w(int(a)); S x(int()); S y((int(a))); S y((int)a); S z = int(a); } — _end example_]

An ambiguity can arise from the similarity between a function-style cast and atype-id.

The resolution is that any construct that could possibly be atype-idin its syntactic context shall be considered atype-id.

[Example 2: template <class T> struct X {};template <int N> struct Y {}; X<int()> a; X<int(1)> b; Y<int()> c; Y<int(1)> d; void foo(signed char a) { sizeof(int()); sizeof(int(a)); sizeof(int(unsigned(a))); (int())+1; (int(a))+1; (int(unsigned(a)))+1; } — _end example_]

Another ambiguity arises in aparameter-declaration-clause when atype-nameis 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 thedeclarator-id.

The resolution is to consider thetype-nameas asimple-type-specifierrather than adeclarator-id.

[Example 3: class C { };void f(int(C)) { } int g(C);void foo() { f(1); f(g); }

For another example,class C { };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 onedeclarator-id; it names the identifier that is declared.

When thedeclarator-idis qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace set of that namespace ([namespace.def])) or to a specialization thereof; the member shall not merely have been introduced by ausing-declarationin the scope of the class or namespace nominated by thenested-name-specifierof thedeclarator-id.

[Note 1:

If the qualifier is the global​::​scope resolution operator, thedeclarator-idrefers to a name declared in the global namespace scope.

— _end note_]

The optional attribute-specifier-seq following a declarator-id appertains to the entity that is declared.

Astatic,thread_­local,extern,mutable,friend,inline,virtual,constexpr, ortypedefspecifier or an explicit-specifierapplies directly to each declarator-idin an init-declarator-list or member-declarator-list; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.

Thus, a declaration of a particular identifier has the formT DwhereTis of the form attribute-specifier-seq decl-specifier-seqandDis a declarator.

Following is a recursive procedure for determining the type specified for the containeddeclarator-idby such a declaration.

First, thedecl-specifier-seqdetermines a type.

In a declarationT Dthedecl-specifier-seq Tdetermines the typeT.

[Example 1:

In the declarationint unsigned i;the type specifiersint unsigneddetermine the type “unsigned int” ([dcl.type.simple]).

— _end example_]

In a declarationattribute-specifier-seq T DwhereDis an unadorned identifier the type of this identifier is “T”.

In a declarationT DwhereDhas the form

( D1 )

the type of the containeddeclarator-idis the same as that of the containeddeclarator-idin the declarationT D1

Parentheses do not alter the type of the embeddeddeclarator-id, but they can alter the binding of complex declarators.

9.3.4.2 Pointers [dcl.ptr]

In a declarationT DwhereDhas the form

* attribute-specifier-seq cv-qualifier-seq D1

and the type of the identifier in the declarationT D1is “derived-declarator-type-list T”, then the type of the identifier ofDis “derived-declarator-type-list cv-qualifier-seq pointer toT”.

Thecv-qualifier_s_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 declarationsconst int ci = 10, *pc = &ci, *const cpc = pc, **ppc;int i, *p, *const cp = &i;declareci, 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; andcp, a constant pointer to integer.

The value ofci,cpc, andcpcannot be changed after initialization.

The value ofpccan be changed, and so can the object pointed to bycp.

Examples of some correct operations arei = ci;*cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;

Examples of ill-formed operations areci = 1; ci++; *pc = 2; cp = &ci; cpc++; p = pc; ppc = &p;

Each is unacceptable because it would either change the value of an object declaredconstor allow it to be changed through a cv-unqualified pointer later, for example:*ppc = &ci; *p = 5;

— _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 hascv-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 declarationT DwhereDhas either of the forms

& attribute-specifier-seq D1
&& attribute-specifier-seq D1

and the type of the identifier in the declarationT D1is “derived-declarator-type-list T”, then the type of the identifier ofDis “derived-declarator-type-list reference toT”.

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 atypedef-name ([dcl.typedef], [temp.param]) ordecltype-specifier ([dcl.type.decltype]), in which case the cv-qualifiers are ignored.

[Example 1: typedef int& A;const A aref = 3;

The type ofarefis “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_]

A declarator that specifies the type “reference to cv void” is ill-formed.

A reference type that is declared using & is called anlvalue reference, and a reference type that is declared using && is called anrvalue 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);declaresato be a reference parameter offso the callf(d)will add3.14tod.

int v[20];int& g(int i) { return v[i]; }g(3) = 7;declares the functiong()to return a reference to an integer sog(3)=7will assign7to the fourth element of the arrayv.

For another example,struct link { link* next;}; link* first;void h(link*& p) { p->next = first; first = p; p = 0;} void k() { link* q = new link; h(q);} declarespto be a reference to a pointer tolinksoh(q)will leaveqwith the value zero.

See also [dcl.init.ref].

— _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 aninitializer ([dcl.init.ref]) except when the declaration contains an explicitexternspecifier ([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].

A reference shall be initialized to refer to a valid object or function.

[Note 2:

In particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a null pointer, which causes undefined behavior.

As described in [class.bit], a reference cannot be bound directly to a bit-field.

— _end note_]

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 3: int i;typedef int& LRI;typedef int&& RRI; LRI& r1 = i; const LRI& r2 = i; const LRI&& r3 = i; RRI& r4 = i; RRI&& r5 = 5; decltype(r2)& r6 = i; decltype(r2)&& r7 = i; — _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]

In a declarationT DwhereDhas the form

and thenested-name-specifierdenotes a class, and the type of the identifier in the declarationT D1is “derived-declarator-type-list T”, then the type of the identifier ofDis “derived-declarator-type-list cv-qualifier-seq pointer to member of classnested-name-specifier of typeT”.

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;declarespmi,pmf,pmdandpmcto be a pointer to a member ofXof typeint, a pointer to a member ofXof typevoid(int), a pointer to a member ofXof typedoubleand a pointer to a member ofYof typecharrespectively.

The declaration ofpmdis well-formed even thoughXhas no members of typedouble.

Similarly, the declaration ofpmcis well-formed even thoughYis an incomplete type.

pmiandpmfcan be used like this:X obj; obj.*pmi = 7; (obj.*pmf)(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-idin the declaration T D1is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of N T”.

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

D1 [ ] attribute-specifier-seq

and the type of the contained declarator-idin the declaration T D1is “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-seqappertains to the array type.

U is called the array element type; this type shall not be a placeholder type ([dcl.spec.auto]), a reference type, a function type, an array of unknown bound, orcv 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; typedef const AA CAA; — _end example_]

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 preceding declaration of the entity in 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[]; int S::y[]; void f() { extern int x[];int i = sizeof(x); } — _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 .

Any of the expressionsx3d,x3d[i],x3d[i][j],x3d[i][j][k]can reasonably appear in an expression.

The expressionx3d[i]is equivalent to*(x3d + i); in that expression,x3dis subject to the array-to-pointer conversion ([conv.array]) and is first converted to a pointer to a 2-dimensional array with rankthat points to the first element of x3d.

Then i is added, which on typical implementations involves multiplyingi by the length of the object to which the pointer points, which is sizeof(int).

The result of the addition and indirection is an lvalue denoting the array element ofx3d(an array of five arrays of seven integers).

If there is another subscript, the same argument applies again, sox3d[i][j] is an lvalue denoting the array element of the array element ofx3d(an array of seven integers), andx3d[i][j][k] is an lvalue denoting the array element of the array element of the array element ofx3d(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 declarationT DwhereDhas the form

and the type of the containeddeclarator-idin the declarationT D1is “derived-declarator-type-list T”, the type of thedeclarator-idinDis “derived-declarator-type-list noexceptfunction of parameter-type-listcv-qualifier-seq ref-qualifierreturning T”, where

The optional attribute-specifier-seqappertains to the function type.

In a declarationT DwhereDhas the form

and the type of the containeddeclarator-idin the declarationT D1is “derived-declarator-type-list T”,T shall be the single type-specifier auto.

The type of thedeclarator-idinDis “derived-declarator-type-list noexceptfunction of parameter-type-listcv-qualifier-seq ref-qualifierreturning U”, where

The optional attribute-specifier-seqappertains to the function type.

A type of either form is a function type.91

The optional attribute-specifier-seq in a parameter-declarationappertains to the parameter.

Theparameter-declaration-clausedetermines the arguments that can be specified, and their processing, when the function is called.

[Note 1:

Theparameter-declaration-clauseis used to convert the arguments specified on the function call; see [expr.call].

— _end note_]

If theparameter-declaration-clauseis 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.

If theparameter-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 declarationint 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 aconst char*.

— _end example_]

The type of a function is determined using the following rules.

The type of each parameter (including function parameter packs) is determined from its owndecl-specifier-seqanddeclarator.

After determining the type of each parameter, any parameterof type “array of T” orof function type Tis adjusted to be “pointer to T”.

After producing the list of parameter types, any top-levelcv-qualifier_s_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'sparameter-type-list.

[Note 3:

This transformation does not affect the types of the parameters.

For example, int(*)(const int p, decltype(p)*) andint(*)(int, const int*) are identical types.

— _end note_]

[Example 2: typedef int FIC(int) const; FIC f; struct S { FIC f; }; FIC S::*pm = &S::f; — _end example_]

The effect of acv-qualifier-seqin 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 4:

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 3: typedef void F();struct S { const F 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 5:

Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions.

— _end note_]

[Example 4:

The declarationint fseek(FILE*, long, int);declares a function taking three arguments of the specified types, and returningint ([dcl.type]).

— _end example_]

A single name can be used for several different functions in a single scope; this is function overloading ([over]).

All declarations for a function shall have equivalent return types, parameter-type-lists, and requires-clauses ([temp.over.link]).

Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things.

There shall be no arrays of functions, although there can be arrays of pointers to functions.

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 5: typedef void F(); F fv; F fv { } void 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 6:

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.

If a parameter name is present in a function declaration that is not a definition, it cannot be used outside of its function declarator because that is the extent of its potential scope ([basic.scope.param]).

— _end note_]

[Example 6:

The declarationint i,*pi, f(),*fpi(int),(*pif)(const char*, const char*),(*fpif(int))(int);declares an integeri, a pointerpito an integer, a functionftaking no arguments and returning an integer, a functionfpitaking an integer argument and returning a pointer to an integer, a pointerpifto a function which takes two pointers to constant characters and returns an integer, a functionfpiftaking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer.

It is especially useful to comparefpiandpif.

The binding of*fpi(int)is*(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a functionfpi, 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 7:

Typedefs and trailing-return-types are sometimes convenient when the return type of a function is complex.

For example, the functionfpifabove could have been declaredtypedef int IFUNC(int); IFUNC* fpif(int);orauto 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 thantemplate <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 8:

A function template is not a function.

— _end note_]

An abbreviated function templateis 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-parameterfor 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.

The invented type template-parameter is a template parameter pack if the corresponding parameter-declarationdeclares 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 7: 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);

These declarations are functionally equivalent (but not equivalent) to the following declarations.

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&); — _end example_]

An abbreviated function template can have a template-head.

[Example 8: 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-declaratorcontaining an ellipsis shall only be used in a parameter-declaration.

When it is part of aparameter-declaration-clause, the parameter-declaration declares a function parameter pack ([temp.variadic]).

A function parameter pack is a pack expansion ([temp.variadic]).

[Example 9: 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 theabstract-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.92

9.3.4.7 Default arguments [dcl.fct.default]

If an initializer-clause is specified in aparameter-declaration thisinitializer-clauseis 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 declarationvoid point(int = 3, int = 4);declares a function that can be called with zero, one, or two arguments of typeint.

It can be called in any of these ways:point(1,2); point(1); point();

The last two calls are equivalent topoint(1,4)andpoint(3,4), respectively.

— _end example_]

A default argument shall not be specified for a template parameter pack or a function parameter pack.

If it is specified in aparameter-declaration-clause, it shall not occur within adeclaratororabstract-declaratorof aparameter-declaration.93

For non-template functions, default arguments can be added in later declarations of a function in the same scope.

Declarations in 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, ...); void f(int, int);void f(int, int = 7);void h() { f(3); void f(int = 1, int); } void m() { void f(int, int); f(4); void f(int, int = 5); f(4); void f(int, int = 5); } void n() { f(6); } template<class ... T> struct C { void f(int n = 0, T...);}; C<int> c; — _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 specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit.

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 bound, and the semantic constraints are checked, at the point where the default argument appears.

Name lookup and checking of semantic constraints for default arguments in function templates and in member functions of class templates are performed as described in [temp.inst].

[Example 3:

In the following code,gwill be called with the valuef(2):int a = 1;int f(int);int g(int x = f(a)); void h() { a = 2;{ int a = 3; g(); } }

— _end example_]

[Note 3:

In member function declarations, names in default arguments are looked up as described in [basic.lookup.unqual].

Access checking applies to names in default arguments as described in [class.access].

— _end note_]

Except for member functions of class templates, 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 class template shall be specified on the initial declaration of the member function within the class template.

[Example 4: class C { void f(int i = 3);void g(int i, int j = 99);};void C::f(int i = 3) {} void C::g(int i = 88, int j) {} — _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); extern void h(int x = sizeof(i)); } — _end example_]

[Note 5:

The keywordthiscannot appear in a default argument of a member function; see [expr.prim.this].

[Example 6: class A { void f(A* p = this) { } }; — _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.

Parameters of a function declared before a default argument are in scope and can hide namespace and class member names.

[Example 7: int a;int f(int a, int b = a); typedef int I;int g(float I, int b = I(2)); int h(int a, int b = sizeof(a)); — _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 ofX​::​mem1()in the following example is ill-formed because no object is supplied for the non-static memberX​::​aused as an initializer.

int b;class X { int a;int mem1(int i = a); int mem2(int i = b); static int b;};

The declaration ofX​::​mem2()is meaningful, however, since no object is needed to access the static memberX​::​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(); } int (*p1)(int) = &f;int (*p2)() = &f; — _end example_]

When a declaration of a function is introduced by way of ausing-declaration ([namespace.udecl]), any default argument information associated with the declaration is made known as well.

If the function is redeclared thereafter in the namespace with additional default arguments, the additional arguments are also known at any point following the redeclaration where theusing-declarationis in scope.

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(); pb->f(); } — _end example_]

9.4 Initializers [dcl.init]

9.4.1 General [dcl.init.general]

The process of initialization described in [dcl.init] applies to all initializations regardless of syntactic context, including the initialization of a function parameter ([expr.call]), the initialization of a return value ([stmt.return]), or when an initializer follows a declarator.

[Note 1:

The rules in [dcl.init] apply even if the grammar permits only the brace-or-equal-initializer form of initializer in a given context.

— _end note_]

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 of a block-scope variable with external or internal linkage that has an initializer is ill-formed.

Tozero-initializean object or reference of typeTmeans:

Todefault-initializean object of typeTmeans:

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

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.

Tovalue-initializean object of typeTmeans:

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.

An initializer for a static member is in the scope of the member's class.

[Example 2: int a;struct X { static int a;static int b;};int X::a = 1;int X::b = a; — _end example_]

If the entity being initialized does not have class type, theexpression-list in a parenthesized initializer shall be a single expression.

The semantics of initializers are as follows.

Thedestination typeis the type of the object or reference being initialized and thesource typeis 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 parenthesized expression-list, the expressions are evaluated in the order specified for function calls ([expr.call]).

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]) might be the declaration within the class definition and not the definition at namespace scope.

— _end note_]

9.4.2 Aggregates [dcl.init.aggr]

[Note 1:

Aggregate initialization does not allow accessing protected and private base class' members or constructors.

— _end note_]

The elements of an aggregate are:

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 elementsof the aggregate are determined as follows:

For each explicitly initialized element:

For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:

If the aggregate is a union and the initializer list is empty, then

[Example 3:

struct S { int a; const char* b; int c; int d = b[a]; }; S ss = { 1, "asdf" };initializesss.awith 1,ss.bwith "asdf",ss.cwith the value of an expression of the formint{}(that is, 0), and ss.d with the value of ss.b[ss.a](that is, 's'), and instruct X { int i, j, k = 42; }; X a[] = { 1, 2, 3, 4, 5, 6 }; X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } }; a and b have the same value

struct A { string a;int b = 42;int c = -1;};

A{.c=21} has the following steps:

— _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 is potentially invoked ([class.dtor]) from the context where the aggregate initialization occurs.

[Note 3:

This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown ([except.ctor]).

— _end note_]

An array of unknown bound initialized with a brace-enclosedinitializer-listcontainingn initializer-clause_s_is defined as havingnelements ([dcl.array]).

[Example 4:

int x[] = { 1, 3, 5 };declares and initializesxas a one-dimensional array that has three elements since no size was specified and there are three initializers.

— _end example_]

An array of unknown bound shall not be initialized with an empty braced-init-list {}.95

[Note 4:

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 5: struct S { int y[] = { 0 }; }; — _end example_]

— _end note_]

[Note 5:

Static data members, non-static data members of anonymous union members, and unnamed bit-fields are not considered elements of the aggregate.

[Example 6: struct A { int i;static int s;int j;int :17;int k;} a = { 1, 2, 3 };

Here, the second initializer 2 initializesa.jand not the static data memberA​::​s, and the third initializer 3 initializes a.kand not the unnamed bit-field before it.

— _end example_]

— _end note_]

Aninitializer-listis ill-formed if the number ofinitializer-clause_s_exceeds the number of elements of the aggregate.

[Example 7:

char cv[4] = { 'a', 's', 'd', 'f', 0 }; is ill-formed.

— _end example_]

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} }; const A& a2 { A{} }; }; A a{a,a}; struct B { int n = B{}.n; }; — _end example_]

If an aggregate class C contains a subaggregate elemente with no elements, the initializer-clause for e shall not be omitted from an initializer-list for an object of typeC unless the initializer-clauses for all elements of C following e are also omitted.

[Example 9: struct S { } s;struct A { S s1;int i1; S s2;int i2; S s3;int i3;} a = { { }, 0, s, 0 }; — _end example_]

When initializing a multi-dimensional array, theinitializer-clause_s_initialize the elements with the last (rightmost) index of the array varying the fastest ([dcl.array]).

[Example 10:

int x[2][2] = { 3, 1, 4, 2 };initializesx[0][0]to3,x[0][1]to1,x[1][0]to4, andx[1][1]to2.

On the other hand,float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } };initializes the first column ofy(regarded as a two-dimensional array) and leaves the rest zero.

— _end example_]

Braces can be elided in aninitializer-listas follows.

If theinitializer-listbegins with a left brace, then the succeeding comma-separated list ofinitializer-clause_s_initializes the elements of a subaggregate; it is erroneous for there to be moreinitializer-clause_s_than elements.

If, however, theinitializer-listfor a subaggregate does not begin with a left brace, then only enoughinitializer-clause_s_from the list are taken to initialize the elements of the subaggregate; any remaininginitializer-clause_s_are left to initialize the next element of the aggregate of which the current subaggregate is an element.

[Example 11:

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 arrayy[0], namelyy[0][0],y[0][1], andy[0][2].

Likewise the next two lines initializey[1]andy[2].

The initializer ends early and thereforey[3]s elements are initialized as if explicitly initialized with an expression of the formfloat(), that is, are initialized with0.0.

In the following example, braces in theinitializer-listare elided; however theinitializer-listhas the same effect as the completely-bracedinitializer-listof the above example,float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };

The initializer forybegins with a left brace, but the one fory[0]does not, therefore three elements from the list are used.

Likewise the next three are taken successively fory[1]andy[2].

— _end example_]

All implicit type conversions ([conv]) are considered when initializing the element with an assignment-expression.

Otherwise, if the element is itself a subaggregate, brace elision is assumed and theassignment-expressionis considered for the initialization of the first element of the subaggregate.

[Note 6:

As specified above, brace elision cannot apply to subaggregates with no elements; aninitializer-clause for the entire subobject is required.

— _end note_]

[Example 12: struct A { int i;operator int();};struct B { A a1, a2;int z;}; A a; B b = { 4, a, a };

Braces are elided around theinitializer-clauseforb.a1.i.

b.a1.iis initialized with 4,b.a2is initialized witha,b.zis initialized with whatevera.operator int()returns.

— _end example_]

[Note 7:

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_]

When a union is initialized with an initializer list, there shall not be more than one explicitly initialized element.

[Example 13: union u { int a; const char* b; }; u a = { 1 }; u b = a; u c = 1; u d = { 0, "asdf" }; u e = { "asdf" }; u f = { .b = "asdf" }; u g = { .a = 1, .b = "asdf" }; — _end example_]

[Note 9:

As described above, the braces around theinitializer-clausefor a union member can be omitted if the union is a member of another aggregate.

— _end note_]

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 can 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]).

Successive characters of the value of the string-literalinitialize the elements of the array.

[Example 1:

char msg[] = "Syntax error on line %s\n";shows a character array whose members are initialized with astring-literal.

Note that because'\n'is a single character and because a trailing'\0'is appended,sizeof(msg)is25.

— _end example_]

There shall not be more initializers than there are array elements.

[Example 2:

char cv[4] = "asdf"; 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 = 1; int* p = &r; int& rr = r; int (&rg)(int) = g; rg(i); int a[3];int (&ra)[3] = a; ra[1] = i; } — _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 theexternspecifier is explicitly used.

[Example 2: int& r1; extern int& r2; — _end example_]

Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is to “cv2 T2” ifT1 is similar ([conv.qual]) to T2, orT1 is a base class of T2.

cv1 T1” is reference-compatiblewith “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:

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_]

9.4.5 List-initialization [dcl.init.list]

List-initialization is initialization of an object or reference from abraced-init-list.

Such an initializer is called an initializer list, and the comma-separatedinitializer-clause_s_of the initializer-listordesignated-initializer-clause_s_of the designated-initializer-listare 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 calleddirect-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.

[Note 1:

List-initialization can be used

[Example 1: int a = {1}; std::complex<double> z{1,2};new std::vectorstd::string\{"once", "upon", "a", "time"}; f( {"Nicholas","Annemarie"} ); return { "Norah" }; int* e {}; x = double{1}; std::mapstd::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 tocv 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 templatestd​::​initializer_­list is not predefined; if the header<initializer_­list> is not imported or included prior to a use ofstd​::​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 T is defined as follows:

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-clausethat 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 theinitializer-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.

Each element of that array is copy-initialized with the corresponding element of the initializer list, and thestd​::​initializer_­list<E> object is constructed to refer to that array.

[Note 5:

A constructor or conversion function selected for the copy is required 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.

[Example 12: struct X { X(std::initializer_list<double> v);}; X x{ 1,2,3 };

The initialization will be implemented in a way roughly equivalent to this:const double __a[3] = {double{1}, double{2}, double{3}}; X x(std::initializer_list<double>(__a, __a+3));assuming that the implementation can construct an initializer_­list object with a pair of pointers.

— _end example_]

The array has the same lifetime as any other temporary object ([class.temporary]), except that initializing aninitializer_­list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.

[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 } {} };

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_]

[Note 6:

The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer could be so allocated.

— _end note_]

A narrowing conversion is an implicit conversion

[Note 7:

As indicated above, such conversions are not allowed at the top level in list-initializations.

— _end note_]

[Example 14: int x = 999; const int y = 999;const int z = 99;char c1 = x; char c2{x}; char c3{y}; char c4{z}; unsigned char uc1 = {5}; unsigned char uc2 = {-1}; unsigned int ui1 = {-1}; signed int si1 = { (unsigned int)-1 }; int ii = {2.0}; float f1 { x }; float f2 { 7 }; bool b = {"meow"}; int f(int);int a[] = { 2, f(2), f(2.0) }; — _end example_]

9.5 Function definitions [dcl.fct.def]

9.5.1 In general [dcl.fct.def.general]

Function definitions have the form

Any informal reference to the body of a function should be interpreted as a reference to the non-terminal function-body.

The optional attribute-specifier-seq in a function-definitionappertains to the 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 unless the function is deleted ([dcl.fct.def.delete]).

[Example 1:

A simple example of a complete function definition isint max(int a, int b, int c) { int m = (a > b) ? a : b;return (m > c) ? m : c;}

Hereintis thedecl-specifier-seq;max(int a, int b, int c)is thedeclarator;{ }is thefunction-body.

— _end example_]

[Note 1:

A cv-qualifier-seq affects the type of thisin the body of a member function; see [expr.prim.this].

— _end note_]

[Note 2:

Unused parameters need not be named.

For example,

void print(int a, int) { std::printf("a = %d\n",a);} — _end note_]

In the function-body, afunction-local predefined variable denotes a block-scope object of static storage duration that is implicitly defined (see [basic.scope.block]).

The function-local predefined variable __func__ is defined as if a definition of the formstatic const char __func__[] = "function-name";had been provided, where function-name is an implementation-defined string.

It is unspecified whether such a variable has an address distinct from that of any other object in the program.97

[Example 2: struct S { S() : s(__func__) { } const char* s;};void f(const char* s = __func__); — _end example_]

9.5.2 Explicitly-defaulted functions [dcl.fct.def.default]

A function definition whosefunction-bodyis of the form= default ;is called an explicitly-defaulted definition.

A function that is explicitly defaulted shall

The type T of an explicitly defaulted special member function Fis allowed to differ from the type T it would have had if it were implicitly declared, as follows:

If T differs from T in any other way, then:

An explicitly-defaulted function that is not defined as deleted may be declaredconstexpr or consteval only if it is constexpr-compatible ([special], [class.compare.default]).

A function explicitly defaulted on its first declaration is implicitly inline ([dcl.inline]), and is implicitly constexpr ([dcl.constexpr]) if it is constexpr-compatible.

[Example 1: struct S { constexpr S() = default; S(int a = 0) = default; void operator=(const S&) = default; ~S() noexcept(false) = default; private: int i; S(S&); }; S::S(S&) = default; struct T { T(); T(T &&) noexcept(false);};struct U { T t; U(); U(U &&) noexcept = default;}; U u1; U u2 = static_cast<U&&>(u1); — _end example_]

Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them ([class.ctor],[class.dtor], [class.copy.ctor], [class.copy.assign]), including possibly defining them as deleted.

A defaulted prospective destructor ([class.dtor]) that is not a destructor is defined as deleted.

A defaulted special member function that is neither a prospective destructor nor an eligible special member function ([special]) is defined as deleted.

A function isuser-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.

[Note 1:

Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base.

— _end note_]

[Example 2: struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default;~trivial() = default;};struct nontrivial1 { nontrivial1();}; nontrivial1::nontrivial1() = default; — _end example_]

9.5.3 Deleted definitions [dcl.fct.def.delete]

A function definition whosefunction-bodyis of the form= delete ;is called a deleted definition.

A function with a deleted definition is also called a deleted function.

A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

[Note 1:

This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function.

It applies even for references in expressions that are not potentially-evaluated.

If a function is overloaded, it is referenced only if the function is selected by overload resolution.

The implicit odr-use ([basic.def.odr]) of a virtual function does not, by itself, constitute a reference.

— _end note_]

[Example 1:

One can prevent default initialization and initialization by non-doubles withstruct onlydouble { onlydouble() = delete; template<class T> onlydouble(T) = delete; onlydouble(double);};

— _end example_]

[Example 2:

One can prevent use of a class in certain new-expressions by using deleted definitions of a user-declared operator new for that class.

struct sometype { void* operator new(std::size_t) = delete;void* operator new[](std::size_t) = delete;}; sometype* p = new sometype; sometype* q = new sometype[3]; — _end example_]

[Example 3:

One can make a class uncopyable, i.e., move-only, by using deleted definitions of the copy constructor and copy assignment operator, and then providing defaulted definitions of the move constructor and move assignment operator.

struct moveonly { moveonly() = default; moveonly(const moveonly&) = delete; moveonly(moveonly&&) = default; moveonly& operator=(const moveonly&) = delete; moveonly& operator=(moveonly&&) = default;~moveonly() = default;}; moveonly* p; moveonly q(*p); — _end example_]

A deleted function is implicitly an inline function ([dcl.inline]).

[Note 2:

The one-definition rule ([basic.def.odr]) applies to deleted definitions.

— _end note_]

A deleted definition of a function shall be the first declaration of the function or, for an explicit specialization of a function template, the first declaration of that specialization.

An implicitly declared allocation or deallocation function ([basic.stc.dynamic]) shall not be defined as deleted.

[Example 4: struct sometype { sometype();}; sometype::sometype() = delete; — _end example_]

9.5.4 Coroutine definitions [dcl.fct.def.coroutine]

The parameter-declaration-clause of the coroutine shall not terminate with an ellipsis that is not part of a parameter-declaration.

[Example 1: task<int> f(); task<void> g1() { int i = co_await f(); std::cout << "f() => " << i << std::endl;} template <typename... Args>task<void> g2(Args&&...) { int i = co_await f(); std::cout << "f() => " << i << std::endl;}task<void> g3(int a, ...) { int i = co_await f(); std::cout << "f() => " << i << std::endl;} — _end example_]

The promise type of a coroutine isstd​::​coroutine_­traits<R, P, …, P>​::​promise_­type, whereR is the return type of the function, and are the sequence of types of the function parameters, preceded by the type of the implicit object parameter ([over.match.funcs]) if the coroutine is a non-static member function.

The promise type shall be a class type.

In the following, is an lvalue of type , where denotes *this and denotes the function parameter for a non-static member function, and denotes the function parameter otherwise.

A coroutine behaves as if its function-body were replaced by:

{
promise-type promise promise-constructor-arguments ;
try {
co_await promise.initial_suspend() ;
function-body
} catch ( ... ) {
if (!initial-await-resume-called)
throw ;
promise.unhandled_exception() ;
}
final-suspend :
co_await promise.final_suspend() ;
}

where

The unqualified-ids return_­voidand return_­value are looked up in the scope of the promise type.

If both are found, the program is ill-formed.

[Note 1:

If the unqualified-id return_­void is found, flowing off the end of a coroutine is equivalent to a co_­return with no operand.

Otherwise, flowing off the end of a coroutine results in undefined behavior ([stmt.return.coroutine]).

— _end note_]

The expression promise.get_­return_­object() is used to initialize the glvalue result or prvalue result object of a call to a coroutine.

The call to get_­return_­objectis sequenced before the call to initial_­suspendand is invoked at most once.

A suspended coroutine can be resumed to continue execution by invoking a resumption member function ([coroutine.handle.resumption]) of a coroutine handle ([coroutine.handle]) that refers to the coroutine.

The function that invoked a resumption member function is called the resumer.

Invoking a resumption member function for a coroutine that is not suspended results in undefined behavior.

An implementation may need to allocate additional storage for a coroutine.

The allocation function's name is looked up in the scope of the promise type.

If this lookup fails, the allocation function's name is looked up in the global scope.

If the lookup finds an allocation function in the scope of the promise type, overload resolution is performed on a function call created by assembling an argument list.

The first argument is the amount of space requested, and has type std​::​size_­t.

The lvalues are the succeeding arguments.

If no viable function is found ([over.match.viable]), overload resolution is performed again on a function call created by passing just the amount of space required as an argument of type std​::​size_­t.

The unqualified-id get_­return_­object_­on_­allocation_­failureis looked up in the scope of the promise type by class member access lookup ([basic.lookup.classref]).

If any declarations are found, then the result of a call to an allocation function used to obtain storage for the coroutine state is assumed to return nullptr if it fails to obtain storage, and if a global allocation function is selected, the ​::​operator new(size_­t, nothrow_­t) form is used.

The allocation function used in this case shall have a non-throwingnoexcept-specifier.

If the allocation function returns nullptr, the coroutine returns control to the caller of the coroutine and the return value is obtained by a call to T​::​get_­return_­object_­on_­allocation_­failure(), where Tis the promise type.

[Example 2: #include <iostream> #include <coroutine> struct generator { struct promise_type;using handle = std::coroutine_handle<promise_type>;struct promise_type { int current_value;static auto get_return_object_on_allocation_failure() { return generator{nullptr}; } auto get_return_object() { return generator{handle::from_promise(*this)}; } auto initial_suspend() { return std::suspend_always{}; } auto final_suspend() noexcept { return std::suspend_always{}; } void unhandled_exception() { std::terminate(); } void return_void() {} auto yield_value(int value) { current_value = value;return std::suspend_always{};} };bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; } int current_value() { return coro.promise().current_value; } generator(generator const&) = delete; generator(generator && rhs) : coro(rhs.coro) { rhs.coro = nullptr; } ~generator() { if (coro) coro.destroy(); } private: generator(handle h) : coro(h) {} handle coro;}; generator f() { co_yield 1; co_yield 2; } int main() { auto g = f();while (g.move_next()) std::cout << g.current_value() << std::endl;} — _end example_]

The coroutine state is destroyed when control flows off the end of the coroutine or the destroy member function ([coroutine.handle.resumption]) of a coroutine handle ([coroutine.handle]) that refers to the coroutine is invoked.

In the latter case objects with automatic storage duration that are in scope at the suspend point are destroyed in the reverse order of the construction.

If destroy is called for a coroutine that is not suspended, the program has undefined behavior.

The deallocation function's name is looked up in the scope of the promise type.

If this lookup fails, the deallocation function's name is looked up in the global scope.

If deallocation function lookup finds both a usual deallocation function with only a pointer parameter and a usual deallocation function with both a pointer parameter and a size parameter, then the selected deallocation function shall be the one with two parameters.

Otherwise, the selected deallocation function shall be the function with one parameter.

If no usual deallocation function is found, the program is ill-formed.

The selected deallocation function shall be called with the address of the block of storage to be reclaimed as its first argument.

If a deallocation function with a parameter of type std​::​size_­t is used, the size of the block is passed as the corresponding argument.

When a coroutine is invoked, after initializing its parameters ([expr.call]), a copy is created for each coroutine parameter.

For a parameter of type cv T, the copy is a variable of type cv Twith automatic storage duration that is direct-initialized from an xvalue of type T referring to the parameter.

The initialization and destruction of each parameter copy occurs in the context of the called coroutine.

Initializations of parameter copies are sequenced before the call to the coroutine promise constructor and indeterminately sequenced with respect to each other.

The lifetime of parameter copies ends immediately after the lifetime of the coroutine promise object ends.

[Note 3:

If a coroutine has a parameter passed by reference, resuming the coroutine after the lifetime of the entity referred to by that parameter has ended is likely to result in undefined behavior.

— _end note_]

If the evaluation of the expression_promise_.unhandled_­exception() exits via an exception, the coroutine is considered suspended at the final suspend point.

The expression co_­await promise.final_­suspend()shall not be potentially-throwing ([except.spec]).

9.6 Structured binding declarations [dcl.struct.bind]

Let cv denote the cv-qualifiers in the decl-specifier-seq and_S_ consist of the storage-class-specifiers of the decl-specifier-seq (if any).

First, a variable with a unique name e is introduced.

If theassignment-expression in the initializerhas array type A and no ref-qualifier is present,e is defined by

attribute-specifier-seq S cv A e ;

and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer.

Otherwise, _e_is defined as-if by

where the declaration is never interpreted as a function declaration and the parts of the declaration other than the declarator-id are taken from the corresponding structured binding declaration.

If the initializer refers to one of the names introduced by the structured binding declaration, the program is ill-formed.

If E is an array type with element type T, the number of elements in the identifier-list shall be equal to the number of elements of E.

Each v is the name of an lvalue that refers to the element i of the array and whose type is T; the referenced type is T.

[Note 2:

The top-level cv-qualifiers of T are cv.

— _end note_]

[Example 1: auto f() -> int(&)[2];auto [ x, y ] = f(); auto& [ xr, yr ] = f(); — _end example_]

Otherwise, if the qualified-id std​::​tuple_­size<E>names a complete class type with a member named value, the expression std​::​tuple_­size<E>​::​valueshall be a well-formed integral constant expression and the number of elements in the identifier-list shall be equal to the value of that expression.

Let i be an index prvalue of type std​::​size_­tcorresponding to .

The unqualified-id get is looked up in the scope of E by class member access lookup ([basic.lookup.classref]), and if that finds at least one declaration that is a function template whose first template parameter is a non-type parameter, the initializer is_e_.get<i>().

Otherwise, the initializer is get<i>(e), where get is looked up in the associated namespaces ([basic.lookup.argdep]).

In either case, get<i> is interpreted as a template-id.

In either case, e is an lvalue if the type of the entity _e_is an lvalue reference and an xvalue otherwise.

Given the type designated bystd​::​tuple_­element<i, E>​::​type and the type designated by either & or &&, where is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise, variables are introduced with unique names as follows:

S U r = initializer ;

Each is the name of an lvalue of type that refers to the object bound to ; the referenced type is .

Otherwise, all of E's non-static data members shall be direct members of E or of the same base class of E, well-formed when named as e._name_in the context of the structured binding,E shall not have an anonymous union member, and the number of elements in the identifier-list shall be equal to the number of non-static data members of E.

Designating the non-static data members of E as, , (in declaration order), each v is the name of an lvalue that refers to the member m of e and whose type is cv , where is the declared type of that member; the referenced type is cv .

The lvalue is a bit-field if that member is a bit-field.

[Example 2: struct S { int x1 : 2; volatile double y1; }; S f();const auto [ x, y ] = f();

The type of the id-expression x is “const int”, the type of the id-expression y is “const volatile double”.

— _end example_]

9.7 Enumerations [enum]

9.7.1 Enumeration declarations [dcl.enum]

An enumeration is a distinct type ([basic.compound]) with named constants.

Its name becomes an enum-name within its scope.

enum-key:
enum
enum class
enum struct

The optional attribute-specifier-seq in the enum-head and the opaque-enum-declaration appertains to the enumeration; the attributes in that attribute-specifier-seq are thereafter considered attributes of the enumeration whenever it is named.

[Note 1:

This resolves a potential ambiguity between the declaration of an enumeration with an enum-base and the declaration of an unnamed bit-field of enumeration type.

[Example 1: struct S { enum E : int {};enum E : int {}; }; — _end example_]

— _end note_]

The enumeration type declared with an enum-keyof only enum is an unscoped enumeration, and its enumerators are unscoped enumerators.

The enum-keys enum class andenum struct are semantically equivalent; an enumeration type declared with one of these is a scoped enumeration, and its enumerators are scoped enumerators.

The optional enum-head-name shall not be omitted in the declaration of a scoped enumeration.

The type-specifier-seq of an enum-baseshall name an integral type; any cv-qualification is ignored.

An opaque-enum-declaration declaring an unscoped enumeration shall not omit the enum-base.

The identifiers in an enumerator-list are declared as constants, and can appear wherever constants are required.

If the first enumeratorhas no initializer, the value of the corresponding constant is zero.

An enumerator-definition without aninitializer gives the enumerator the value obtained by increasing the value of the previous enumeratorby one.

[Example 2:

enum { a, b, c=0 };enum { d, e, f=e+2 };defines a, c, and d to be zero, b ande to be 1, and f to be 3.

— _end example_]

The optional attribute-specifier-seq in anenumerator appertains to that enumerator.

An opaque-enum-declaration is either a redeclaration of an enumeration in the current scope or a declaration of a new enumeration.

[Note 2:

An enumeration declared by anopaque-enum-declaration has a fixed underlying type and is a complete type.

The list of enumerators can be provided in a later redeclaration with an enum-specifier.

— _end note_]

A scoped enumeration shall not be later redeclared as unscoped or with a different underlying type.

An unscoped enumeration shall not be later redeclared as scoped and each redeclaration shall include an enum-base specifying the same underlying type as in the original declaration.

If an enum-head-name contains anested-name-specifier, it shall not begin with a decltype-specifier and the enclosing enum-specifieror opaque-enum-declaration shall refer to an enumeration that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set ([namespace.def]) of that namespace (i.e., neither inherited nor introduced by a using-declaration), and theenum-specifier or opaque-enum-declarationshall appear in a namespace enclosing the previous declaration.

Each enumeration defines a type that is different from all other types.

Each enumeration also has an underlying type.

The underlying type can be explicitly specified using an enum-base.

For a scoped enumeration type, the underlying type is int if it is not explicitly specified.

In both of these cases, the underlying type is said to befixed.

Following the closing brace of an enum-specifier, each enumerator has the type of its enumeration.

If the underlying type is fixed, the type of each enumerator prior to the closing brace is the underlying type and the constant-expression in the enumerator-definitionshall be aconverted constant expressionof the underlying type.

If the underlying type is not fixed, the type of each enumerator prior to the closing brace is determined as follows:

An enumeration whose underlying type is fixed is an incomplete type from itspoint of declaration to immediately after itsenum-base (if any), at which point it becomes a complete type.

An enumeration whose underlying type is not fixed is an incomplete type from its point of declaration to immediately after the closing } of itsenum-specifier, at which point it becomes a complete type.

For an enumeration whose underlying type is not fixed, the underlying type is an integral type that can represent all the enumerator values defined in the enumeration.

If no integral type can represent all the enumerator values, the enumeration is ill-formed.

It is implementation-defined which integral type is used as the underlying type except that the underlying type shall not be larger than intunless the value of an enumerator cannot fit in an int orunsigned int.

If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0.

For an enumeration whose underlying type is fixed, the values of the enumeration are the values of the underlying type.

Otherwise, the values of the enumeration are the values representable by a hypothetical integer type with minimal width Msuch that all enumerators can be represented.

The width of the smallest bit-field large enough to hold all the values of the enumeration type is M.

It is possible to define an enumeration that has values not defined by any of its enumerators.

If the enumerator-list is empty, the values of the enumeration are as if the enumeration had a single enumerator with value 0.98

Two enumeration types are layout-compatible enumerationsif they have the same underlying type.

The value of an enumerator or an object of an unscoped enumeration type is converted to an integer by integral promotion.

[Example 3:

enum color { red, yellow, green=20, blue }; color col = red; color* cp = &col;if (*cp == blue) makes color a type describing various colors, and then declarescol as an object of that type, and cp as a pointer to an object of that type.

The possible values of an object of typecolor are red, yellow, green,blue; these values can be converted to the integral values0, 1, 20, and 21.

Since enumerations are distinct types, objects of type color can be assigned only values of type color.

color c = 1; int i = yellow;

Note that this implicit enum to intconversion is not provided for a scoped enumeration:enum class Col { red, yellow, green };int x = Col::red; Col y = Col::red;if (y) { }

— _end example_]

Each enum-name and each unscoped enumerator is declared in the scope that immediately contains the enum-specifier.

Each scoped enumerator is declared in the scope of the enumeration.

An unnamed enumeration that does not have a typedef name for linkage purposes ([dcl.typedef]) and that has a first enumerator is denoted, for linkage purposes ([basic.link]), by its underlying type and its first enumerator; such an enumeration is said to have an enumerator as a name for linkage purposes.

[Note 3:

Each unnamed enumeration with no enumerators is a distinct type.

— _end note_]

[Example 4: enum direction { left='l', right='r' };void g() { direction d; d = left; d = direction::right; } enum class altitude { high='h', low='l' };void h() { altitude a; a = high; a = altitude::low; } — _end example_]

[Note 4:

An enumerator declared in class scope can be referred to using the class member access operators . (dot) and ->(arrow) ([expr.ref]), or with the scope resolution operator ​::​ ([class.qual]).

[Example 5: struct X { enum direction { left='l', right='r' };int f(int i) { return i==left ? 0 : i==right ? 1 : 2; } };void g(X* p) { direction d; int i; i = p->f(left); i = p->f(X::right); i = p->f(p->left); } — _end example_]

— _end note_]

9.7.2 The using enum declaration [enum.udecl]

The elaborated-enum-specifiershall not name a dependent type and the type shall have a reachable enum-specifier.

A using-enum-declarationintroduces the enumerator names of the named enumeration as if by a using-declaration for each enumerator.

[Note 1:

A using-enum-declaration in class scope adds the enumerators of the named enumeration as members to the scope.

This means they are accessible for member lookup.

[Example 1: enum class fruit { orange, apple };struct S { using enum fruit; };void f() { S s; s.orange; S::orange; } — _end example_]

— _end note_]

[Note 2:

Two using-enum-declaration_s_that introduce two enumerators of the same name conflict.

[Example 2: enum class fruit { orange, apple };enum class color { red, orange };void f() { using enum fruit; using enum color; } — _end example_]

— _end note_]

9.8 Namespaces [basic.namespace]

9.8.1 General [basic.namespace.general]

A namespace is an optionally-named declarative region.

The name of a namespace can be used to access entities declared in that namespace; that is, the members of the namespace.

Unlike other declarative regions, the definition of a namespace can be split over several parts of one or more translation units.

[Note 1:

A namespace is never attached to a module, and never has module linkage even if it is not exported.

— _end note_]

[Example 1: export module M;namespace N1 {} export namespace N2 {} namespace N3 { export int n; } — _end example_]

9.8.2 Namespace definition [namespace.def]

9.8.2.1 General [namespace.def.general]

In a named-namespace-definition, the identifier is the name of the namespace.

If the identifier, when looked up, refers to a namespace-name (but not a namespace-alias) that was introduced in the namespace in which the named-namespace-definition appears or that was introduced in a member of the inline namespace set of that namespace, the namespace-definition extends the previously-declared namespace.

Otherwise, the identifier is introduced as a namespace-name into the declarative region in which the named-namespace-definition appears.

Because a namespace-definition containsdeclarations in its namespace-body and anamespace-definition is itself a declaration, it follows that namespace-definitions can be nested.

[Example 1: namespace Outer { int i;namespace Inner { void f() { i++; } int i;void g() { i++; } } } — _end example_]

The enclosing namespaces of a declaration are those namespaces in which the declaration lexically appears, except for a redeclaration of a namespace member outside its original namespace (e.g., a definition as specified in [namespace.memdef]).

Such a redeclaration has the same enclosing namespaces as the original declaration.

[Example 2: namespace Q { namespace V { void f(); class C { void m(); };} void V::f() { extern void h(); } void V::C::m() { } } — _end example_]

If the optional initial inline keyword appears in anamespace-definition for a particular namespace, that namespace is declared to be an inline namespace.

The inline keyword may be used on a namespace-definition that extends a namespace only if it was previously used on the namespace-definitionthat initially declared the namespace-name for that namespace.

The optional attribute-specifier-seqin a named-namespace-definitionappertains to the namespace being defined or extended.

Members of an inline namespace can be used in most respects as though they were members of the enclosing namespace.

Specifically, the inline namespace and its enclosing namespace are both added to the set of associated namespaces used inargument-dependent lookup whenever one of them is, and a using-directive ([namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace.

Finally, looking up a name in the enclosing namespace via explicit qualification ([namespace.qual]) will include members of the inline namespace brought in by the using-directive even if there are declarations of that name in the enclosing namespace.

These properties are transitive: if a namespace N contains an inline namespaceM, which in turn contains an inline namespace O, then the members ofO can be used as though they were members of M or N.

The inline namespace set of N is the transitive closure of all inline namespaces in N.

The enclosing namespace set of O is the set of namespaces consisting of the innermost non-inline namespace enclosing an inline namespace O, together with any intervening inline namespaces.

A nested-namespace-definition with anenclosing-namespace-specifier E,identifier I andnamespace-body Bis equivalent tonamespace E { inline namespace I { B } } where the optional inline is present if and only if the identifier I is preceded by inline.

[Example 3: namespace A::inline B::C { int i;}

The above has the same effect as:namespace A { inline namespace B { namespace C { int i;} } }

— _end example_]

9.8.2.2 Unnamed namespaces [namespace.unnamed]

An unnamed-namespace-definition behaves as if it were replaced by

inline namespace unique { }
using namespace unique ;
namespace unique { namespace-body }

whereinline appears if and only if it appears in theunnamed-namespace-definitionand all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the translation unit.

The optional attribute-specifier-seqin the unnamed-namespace-definitionappertains to unique.

[Example 1: namespace { int i; } void f() { i++; } namespace A { namespace { int i; int j; } void g() { i++; } } using namespace A;void h() { i++; A::i++; j++; } — _end example_]

9.8.2.3 Namespace member definitions [namespace.memdef]

[Note 1:

An explicit instantiation ([temp.explicit]) or explicit specialization ([temp.expl.spec]) of a template does not introduce a name and thus can be declared using anunqualified-id in a member of the enclosing namespace set, if the primary template is declared in an inline namespace.

— _end note_]

[Example 1: namespace X { void f() { } namespace M { void g(); } using M::g;void g(); } — _end example_]

Members of a named namespace can also be defined outside that namespace by explicit qualification ([namespace.qual]) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration's namespace.

[Example 2: namespace Q { namespace V { void f();} void V::f() { } void V::g() { } namespace V { void g();} } namespace R { void Q::V::g() { } } — _end example_]

If a friend declaration in a non-local class first declares a class, function, class template or function template99the friend is a member of the innermost enclosing namespace.

[Note 2:

The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship).

— _end note_]

If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments ([basic.lookup.argdep]).

If the name in a friend declaration is neither qualified nor atemplate-id and the declaration is a function or anelaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

[Note 3:

The other forms of friend declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules.

— _end note_]

[Example 3: void h(int);template <class T> void f2(T);namespace A { class X { friend void f(X); class Y { friend void g(); friend void h(int); friend void f2<>(int); };}; X x;void g() { f(x); } void f(X) { } void h(int) { } } using A::x;void h() { A::f(x); A::X::f(x); A::X::Y::g(); } — _end example_]

9.8.3 Namespace alias [namespace.alias]

A namespace-alias-definition declares an alternate name for a namespace according to the following grammar:

The identifier in a namespace-alias-definition is a synonym for the name of the namespace denoted by thequalified-namespace-specifier and becomes anamespace-alias.

In a declarative region, a namespace-alias-definition can be used to redefine a namespace-alias declared in that declarative region to refer only to the namespace to which it already refers.

[Example 1:

The following declarations are well-formed:namespace Company_with_very_long_name { } namespace CWVLN = Company_with_very_long_name;namespace CWVLN = Company_with_very_long_name; namespace CWVLN = CWVLN;

— _end example_]

9.8.4 Using namespace directive [namespace.udir]

A using-directive shall not appear in class scope, but may appear in namespace scope or in block scope.

The optional attribute-specifier-seq appertains to the using-directive.

A using-directive specifies that the names in the nominated namespace can be used in the scope in which theusing-directive appears after the using-directive.

During unqualified name lookup ([basic.lookup.unqual]), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

[Note 2:

In this context, “contains” means “contains directly or indirectly”.

— _end note_]

A using-directive does not add any members to the declarative region in which it appears.

[Example 1: namespace A { int i;namespace B { namespace C { int i;} using namespace A::B::C;void f1() { i = 5; } } namespace D { using namespace B;using namespace C;void f2() { i = 5; } } void f3() { i = 5; } } void f4() { i = 5; } — _end example_]

For unqualified lookup ([basic.lookup.unqual]), theusing-directive is transitive: if a scope contains ausing-directive that nominates a second namespace that itself contains using-directives, the effect is as if theusing-directives from the second namespace also appeared in the first.

[Example 2: namespace M { int i;} namespace N { int i;using namespace M;} void f() { using namespace N; i = 7; }

For another example,namespace A { int i;} namespace B { int i;int j;namespace C { namespace D { using namespace A;int j;int k;int a = i; } using namespace D;int k = 89; int l = k; int m = i; int n = j; } }

— _end example_]

If a namespace is extended ([namespace.def]) after ausing-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extending namespace-definition can be used after the extending namespace-definition.

[Note 4:

If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions or function templates, the use of the name is ill-formed ([basic.lookup]).

In particular, the name of a variable, function or enumerator does not hide the name of a class or enumeration declared in a different namespace.

For example,namespace A { class X { };extern "C" int g();extern "C++" int h();} namespace B { void X(int);extern "C" int g();extern "C++" int h(int);} using namespace A;using namespace B;void f() { X(1); g(); h(); }

— _end note_]

During overload resolution, all functions from the transitive search are considered for argument matching.

The set of declarations found by the transitive search is unordered.

[Note 5:

In particular, the order in which namespaces were considered and the relationships among the namespaces implied by theusing-directives do not cause preference to be given to any of the declarations found by the search.

— _end note_]

An ambiguity exists if the best match finds two functions with the same signature, even if one is in a namespace reachable throughusing-directives in the namespace of the other.100

[Example 3: namespace D { int d1;void f(char);} using namespace D;int d1; namespace E { int e;void f(int);} namespace D { int d2;using namespace E;void f(int);} void f() { d1++; ::d1++; D::d1++; d2++; e++; f(1); f('a'); } — _end example_]

9.9 The using declaration [namespace.udecl]

Each using-declarator in a using-declaration101introduces a set of declarations into the declarative region in which the using-declaration appears.

The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.

If the using-declarator does not name a constructor, the unqualified-id is declared in the declarative region in which the using-declaration appears as a synonym for each declaration introduced by the using-declarator.

[Note 1:

Only the specified name is so declared; specifying an enumeration name in a using-declarationdoes not declare its enumerators in the using-declaration's declarative region.

— _end note_]

If the using-declarator names a constructor, it declares that the class inherits the set of constructor declarations introduced by the using-declarator from the nominated base class.

Every using-declaration is a declaration and amember-declaration and can therefore be used in a class definition.

[Example 1: struct B { void f(char);void g(char);enum E { e };union { int x; };};struct D : B { using B::f;void f(int) { f('c'); } void g(int) { g('c'); } }; — _end example_]

[Example 2: enum class button { up, down };struct S { using button::up; button b = up; }; — _end example_]

If ausing-declarator names a constructor, itsnested-name-specifier shall name a direct base class of the class being defined.

[Example 3: template <typename... bases> struct X : bases... { using bases::g...;}; X<B, D> x; — _end example_]

[Example 4: class C { int g();};class D2 : public B { using B::f; using B::e; using B::x; using C::g; }; — _end example_]

[Note 2:

Since destructors do not have names, ausing-declaration cannot refer to a destructor for a base class.

Since specializations of member templates for conversion functions are not found by name lookup, they are not considered when a using-declaration specifies a conversion function ([temp.mem]).

— _end note_]

If a constructor or assignment operator brought from a base class into a derived class has the signature of a copy/move constructor or assignment operator for the derived class ([class.copy.ctor], [class.copy.assign]), the using-declaration does not by itself suppress the implicit declaration of the derived class member; the member from the base class is hidden or overridden by the implicitly-declared copy/move constructor or assignment operator of the derived class, as described below.

[Example 5: struct A { template <class T> void f(T);template <class T> struct X { };};struct B : A { using A::f<double>; using A::X<int>; }; — _end example_]

A using-declaration shall not name a namespace.

A using-declaration that names a class member other than an enumerator shall be amember-declaration.

[Example 6: struct X { int i;static int s;};void f() { using X::i; using X::s; } — _end example_]

Members declared by a using-declaration can be referred to by explicit qualification just like other member names ([namespace.qual]).

[Example 7: void f();namespace A { void g();} namespace X { using ::f; using A::g; } void h() { X::f(); X::g(); } — _end example_]

A using-declaration is a declaration and can therefore be used repeatedly where (and only where) multiple declarations are allowed.

[Example 8: namespace A { int i;} namespace A1 { using A::i, A::i; } struct B { int i;};struct X : B { using B::i, B::i; }; — _end example_]

[Note 3:

For a using-declarationwhose nested-name-specifier names a namespace, members added to the namespace after the using-declarationare not in the set of introduced declarations, so they are not considered when a use of the name is made.

Thus, additional overloads added after the using-declaration are ignored, but default function arguments ([dcl.fct.default]), default template arguments ([temp.param]), and template specializations ([temp.class.spec],[temp.expl.spec]) are considered.

— _end note_]

[Example 9: namespace A { void f(int);} using A::f; namespace A { void f(char);} void foo() { f('a'); } void bar() { using A::f; f('a'); } — _end example_]

[Note 4:

Partial specializations of class templates are found by looking up the primary class template and then considering all partial specializations of that template.

If a using-declaration names a class template, partial specializations introduced after theusing-declaration are effectively visible because the primary template is visible ([temp.class.spec]).

— _end note_]

Since a using-declaration is a declaration, the restrictions on declarations of the same name in the samedeclarative region also apply to using-declarations.

[Example 10: namespace A { int x;} namespace B { int i;struct g { };struct x { };void f(int);void f(double);void g(char); } void func() { int i;using B::i; void f(char);using B::f; f(3.5); using B::g; g('a'); struct g g1; using B::x;using A::x; x = 99; struct x x1; } — _end example_]

If a function declaration in namespace scope or block scope has the same name and the same parameter-type-list as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.

If a function template declaration in namespace scope has the same name, parameter-type-list, trailing requires-clause (if any), return type, andtemplate-head, as a function template introduced by ausing-declaration, the program is ill-formed.

[Note 5:

Two using-declarations can introduce functions with the same name and the same parameter-type-list.

If, for a call to an unqualified function name, function overload resolution selects the functions introduced by such using-declarations, the function call is ill-formed.

[Example 11: namespace B { void f(int);void f(double);} namespace C { void f(int);void f(double);void f(char);} void h() { using B::f; using C::f; f('h'); f(1); void f(int); } — _end example_]

— _end note_]

When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list ([dcl.fct]), trailing requires-clause (if any), cv-qualification, andref-qualifier (if any), in a base class (rather than conflicting).

Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.

[Example 12: struct B { virtual void f(int);virtual void f(char);void g(int);void h(int);};struct D : B { using B::f;void f(int); using B::g;void g(char); using B::h;void h(int); };void k(D* p) { p->f(1); p->f('a'); p->g(1); p->g('a'); } struct B1 { B1(int);};struct B2 { B2(int);};struct D1 : B1, B2 { using B1::B1;using B2::B2;}; D1 d1(0); struct D2 : B1, B2 { using B1::B1;using B2::B2; D2(int); }; D2 d2(0); — _end example_]

[Note 6:

For the purpose of forming a set of candidates during overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class ([class.member.lookup]).

In particular, the implicit object parameter is treated as if it were a reference to the derived class rather than to the base class ([over.match.funcs]).

This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

— _end note_]

Constructors that are introduced by a using-declarationare treated as though they were constructors of the derived class when looking up the constructors of the derived class ([class.qual]) or forming a set of overload candidates ([over.match.ctor], [over.match.copy], [over.match.list]).

[Note 7:

If such a constructor is selected to perform the initialization of an object of class type, all subobjects other than the base class from which the constructor originated are implicitly initialized ([class.inhctor.init]).

A constructor of a derived class is sometimes preferred to a constructor of a base class if they would otherwise be ambiguous ([over.match.best]).

— _end note_]

In a using-declarator that does not name a constructor, all members of the set of introduced declarations shall be accessible.

In a using-declarator that names a constructor, no access check is performed.

In particular, if a derived class uses ausing-declarator to access a member of a base class, the member name shall be accessible.

If the name is that of an overloaded member function, then all functions named shall be accessible.

The base class members mentioned by a using-declarator shall be visible in the scope of at least one of the direct base classes of the class where the using-declarator is specified.

[Note 8:

Because a using-declarator designates a base class member (and not a member subobject or a member function of a base class subobject), a using-declarator cannot be used to resolve inherited member ambiguities.

[Example 13: struct A { int x(); };struct B : A { };struct C : A { using A::x;int x(int);};struct D : B, C { using C::x;int x(double);};int f(D* d) { return d->x(); } — _end example_]

— _end note_]

A synonym created by a using-declaration has the usual accessibility for a member-declaration.

A using-declarator that names a constructor does not create a synonym; instead, the additional constructors are accessible if they would be accessible when used to construct an object of the corresponding base class, and the accessibility of the using-declaration is ignored.

[Example 14: class A { private: void f(char);public: void f(int);protected: void g();};class B : public A { using A::f; public: using A::g; }; — _end example_]

If a using-declarator uses the keyword typename and specifies a dependent name ([temp.dep]), the name introduced by theusing-declaration is treated as atypedef-name ([dcl.typedef]).

9.10 The asm declaration [dcl.asm]

An asm declaration has the form

The asm declaration is conditionally-supported; its meaning isimplementation-defined.

The optional attribute-specifier-seq in an asm-declaration appertains to the asm declaration.

[Note 1:

Typically it is used to pass information through the implementation to an assembler.

— _end note_]

All function types, function names with external linkage, and variable names with external linkage have a language linkage.

[Note 1:

Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here.

For example, a particular language linkage might be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc.

— _end note_]

The default language linkage of all function types, function names, and variable names is C++ language linkage.

Two function types with different language linkages are distinct types even if they are otherwise identical.

Linkage between C++ and non-C++ code fragments can be achieved using a linkage-specification:

The string-literal indicates the required language linkage.

This document specifies the semantics for thestring-literals "C" and "C++".

Use of astring-literal other than "C" or "C++" is conditionally-supported, with implementation-defined semantics.

[Note 2:

Therefore, a linkage-specification with a string-literal that is unknown to the implementation requires a diagnostic.

— _end note_]

[Note 3:

It is recommended that the spelling of the string-literal be taken from the document defining that language.

For example, Ada(not ADA) and Fortran or FORTRAN, depending on the vintage.

— _end note_]

Every implementation shall provide for linkage to functions written in the C programming language,"C", and linkage to C++ functions, "C++".

[Example 1: complex sqrt(complex); extern "C" { double sqrt(double); } — _end example_]

A module-import-declaration appearing in a linkage specification with other than C++ language linkage is conditionally-supported withimplementation-defined semantics.

Linkage specifications nest.

When linkage specifications nest, the innermost one determines the language linkage.

A linkage specification does not establish a scope.

In alinkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification.

[Example 2: extern "C" void f1(void(*pf)(int)); extern "C" typedef void FUNC(); FUNC f2; extern "C" FUNC f3; void (*pf2)(FUNC*); extern "C" { static void f4(); } extern "C" void f5() { extern void f4(); } extern void f4(); void f6() { extern void f4(); } — _end example_]

A C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions.

[Example 3: extern "C" typedef void FUNC_c();class C { void mf1(FUNC_c*); FUNC_c mf2; static FUNC_c* q; };extern "C" { class X { void mf(); void mf2(void(*)()); };} — _end example_]

If two declarations declare functions with the same name andparameter-type-list to be members of the same namespace or declare objects with the same name to be members of the same namespace and the declarations give the names different language linkages, the program is ill-formed; no diagnostic is required if the declarations appear in different translation units.

Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function.

A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.

At most one function with a particular name can have C language linkage.

Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function.

Two declarations for a variable with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same variable.

An entity with C language linkage shall not be declared with the same name as a variable in global scope, unless both declarations denote the same entity; no diagnostic is required if the declarations appear in different translation units.

A variable with C language linkage shall not be declared with the same name as a function with C language linkage (ignoring the namespace names that qualify the respective names); no diagnostic is required if the declarations appear in different translation units.

[Note 4:

Only one definition for an entity with a given name with C language linkage can appear in the program (see [basic.def.odr]); this implies that such an entity must not be defined in more than one namespace scope.

— _end note_]

[Example 4: int x;namespace A { extern "C" int f();extern "C" int g() { return 1; } extern "C" int h();extern "C" int x(); } namespace B { extern "C" int f(); extern "C" int g() { return 1; } } int A::f() { return 98; } extern "C" int h() { return 97; } — _end example_]

A declaration directly contained in alinkage-specificationis treated as if it contains theexternspecifier for the purpose of determining the linkage of the declared name and whether it is a definition.

Such a declaration shall not specify a storage class.

[Example 5: extern "C" double f();static double f(); extern "C" int i; extern "C" { int i; } extern "C" static void g(); — _end example_]

[Note 5:

Because the language linkage is part of a function type, when indirecting through a pointer to C function, the function to which the resulting lvalue refers is considered a C function.

— _end note_]

Linkage from C++ to objects defined in other languages and to objects defined in C++ from other languages is implementation-defined and language-dependent.

Only where the object layout strategies of two language implementations are similar enough can such linkage be achieved.

9.12 Attributes [dcl.attr]

9.12.1 Attribute syntax and semantics [dcl.attr.grammar]

Attributes specify additional information for various source constructs such as types, variables, names, blocks, or translation units.

balanced-token:
( balanced-token-seq )
[ balanced-token-seq ]
{ balanced-token-seq }
any token other than a parenthesis, a bracket, or a brace

If an attribute-specifiercontains an attribute-using-prefix, the attribute-list following that attribute-using-prefixshall not contain an attribute-scoped-tokenand every attribute-token in that attribute-listis treated as if its identifier were prefixed with N​::​, where N is the attribute-namespacespecified in the attribute-using-prefix.

[Note 1:

This rule imposes no constraints on how an attribute-using-prefixaffects the tokens in an attribute-argument-clause.

— _end note_]

[Example 1: [[using CC: opt(1), debug]] void f() {} [[using CC: opt(1)]] [[CC::debug]] void g() {} [[using CC: CC::opt(1)]] void h() {} — _end example_]

[Note 2:

For each individual attribute, the form of thebalanced-token-seq will be specified.

— _end note_]

In an attribute-list, an ellipsis may appear only if thatattribute's specification permits it.

An attribute-specifier that contains no attributes has no effect.

The order in which the attribute-tokens appear in anattribute-list is not significant.

Noname lookup is performed on any of the identifiers contained in anattribute-token.

The attribute-token determines additional requirements on the attribute-argument-clause (if any).

Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears ([stmt.stmt], [dcl.dcl],[dcl.decl]).

If an attribute-specifier-seq that appertains to some entity or statement contains an attribute or alignment-specifier that is not allowed to apply to that entity or statement, the program is ill-formed.

If an attribute-specifier-seqappertains to a friend declaration ([class.friend]), that declaration shall be a definition.

[Note 3:

An attribute-specifier-seq cannot appeartain to an explicit instantiation ([temp.explicit]).

— _end note_]

For an attribute-token(including an attribute-scoped-token) not specified in this document, the behavior is implementation-defined.

Any attribute-token that is not recognized by the implementation is ignored.

An attribute-token is reserved for future standardization if

Each implementation should choose a distinctive name for theattribute-namespace in an attribute-scoped-token.

Two consecutive left square bracket tokens shall appear only when introducing an attribute-specifier or within the balanced-token-seq of an attribute-argument-clause.

[Note 4:

If two consecutive left square brackets appear where an attribute-specifier is not allowed, the program is ill-formed even if the brackets match an alternative grammar production.

— _end note_]

[Example 2: int p[10];void f() { int x = 42, y[5];int(p[[x] { return x; }()]); y[[] { return 2; }()] = 2; int i [[vendor::attr([[]])]]; } — _end example_]

9.12.2 Alignment specifier [dcl.align]

When the alignment-specifier is of the formalignas( constant-expression ):

An alignment-specifier of the formalignas( type-id ) has the same effect as alignas(​alignof( type-id )).

The alignment requirement of an entity is the strictest nonzero alignment specified by its alignment-specifiers, if any; otherwise, the alignment-specifiers have no effect.

The combined effect of all alignment-specifiers in a declaration shall not specify an alignment that is less strict than the alignment that would be required for the entity being declared if all alignment-specifier_s_appertaining to that entity were omitted.

[Example 1: struct alignas(8) S {};struct alignas(1) U { S s;}; — _end example_]

If the defining declaration of an entity has analignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have noalignment-specifier.

Conversely, if any declaration of an entity has analignment-specifier, every defining declaration of that entity shall specify an equivalent alignment.

No diagnostic is required if declarations of an entity have different alignment-specifier_s_in different translation units.

[Example 2: struct S { int x; } s, *p = &s;struct alignas(16) S; extern S* p; — _end example_]

[Example 3:

An aligned buffer with an alignment requirement of A and holding N elements of type Tcan be declared as:alignas(T) alignas(A) T buffer[N];

Specifying alignas(T) ensures that the final requested alignment will not be weaker than alignof(T), and therefore the program will not be ill-formed.

— _end example_]

[Example 4: alignas(double) void f(); alignas(double) unsigned char c[sizeof(double)]; extern unsigned char c[sizeof(double)]; alignas(float) extern unsigned char c[sizeof(double)]; — _end example_]

9.12.3 Carries dependency attribute [dcl.attr.depend]

The attribute-token carries_­dependency specifies dependency propagation into and out of functions.

It shall appear at most once in each attribute-list and noattribute-argument-clause shall be present.

The attribute may be applied to the declarator-id of aparameter-declaration in a function declaration or lambda, in which case it specifies that the initialization of the parametercarries a dependency to each lvalue-to-rvalue conversion of that object.

The attribute may also be applied to the declarator-id of a function declaration, in which case it specifies that the return value, if any, carries a dependency to the evaluation of the function call expression.

The first declaration of a function shall specify the carries_­dependency attribute for itsdeclarator-id if any declaration of the function specifies thecarries_­dependency attribute.

Furthermore, the first declaration of a function shall specify the carries_­dependency attribute for a parameter if any declaration of that function specifies the carries_­dependency attribute for that parameter.

If a function or one of its parameters is declared with the carries_­dependency attribute in its first declaration in one translation unit and the same function or one of its parameters is declared without thecarries_­dependency attribute in its first declaration in another translation unit, the program is ill-formed, no diagnostic required.

[Note 1:

The carries_­dependency attribute does not change the meaning of the program, but might result in generation of more efficient code.

— _end note_]

[Example 1: struct foo { int* a; int* b; }; std::atomic<struct foo *> foo_head[10];int foo_array[10][10];[[carries_dependency]] struct foo* f(int i) { return foo_head[i].load(memory_order::consume);} int g(int* x, int* y [[carries_dependency]]) { return kill_dependency(foo_array[*x][*y]);} [[carries_dependency]] struct foo* f(int i);int g(int* x, int* y [[carries_dependency]]);int c = 3;void h(int i) { struct foo* p; p = f(i); do_something_with(g(&c, p->a)); do_something_with(g(p->a, &c));}

The carries_­dependency attribute on function f means that the return value carries a dependency out of f, so that the implementation need not constrain ordering upon return from f.

Implementations off and its caller may choose to preserve dependencies instead of emitting hardware memory ordering instructions (a.k.a. fences).

Function g's second parameter has a carries_­dependency attribute, but its first parameter does not.

Therefore, function h's first call tog carries a dependency into g, but its second call does not.

The implementation might need to insert a fence prior to the second call tog.

— _end example_]

9.12.4 Deprecated attribute [dcl.attr.deprecated]

The attribute-token deprecated can be used to mark names and entities whose use is still allowed, but is discouraged for some reason.

[Note 1:

In particular,deprecated is appropriate for names and entities that are deemed obsolescent or unsafe.

— _end note_]

It shall appear at most once in each attribute-list.

Anattribute-argument-clause may be present and, if present, it shall have the form:

[Note 2:

The string-literal in the attribute-argument-clausecan be used to explain the rationale for deprecation and/or to suggest a replacing entity.

— _end note_]

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, a namespace, an enumeration, an enumerator, or a template specialization.

A name or entity declared without the deprecated attribute can later be redeclared with the attribute and vice-versa.

[Note 3:

Thus, an entity initially declared without the attribute can be marked as deprecated by a subsequent redeclaration.

However, after an entity is marked as deprecated, later redeclarations do not un-deprecate the entity.

— _end note_]

Redeclarations using different forms of the attribute (with or without theattribute-argument-clause or with differentattribute-argument-clauses) are allowed.

Recommended practice: Implementations should use the deprecated attribute to produce a diagnostic message in case the program refers to a name or entity other than to declare it, after a declaration that specifies the attribute.

The diagnostic message should include the text provided within the attribute-argument-clause of any deprecated attribute applied to the name or entity.

9.12.5 Fallthrough attribute [dcl.attr.fallthrough]

The attribute-token fallthroughmay be applied to a null statement;such a statement is a fallthrough statement.

The attribute-token fallthroughshall appear at most once in each attribute-list and no attribute-argument-clause shall be present.

A fallthrough statement may only appear within an enclosing switch statement.

The next statement that would be executed after a fallthrough statement shall be a labeled statement whose label is a case label or default label for the same switch statement and, if the fallthrough statement is contained in an iteration statement, the next statement shall be part of the same execution of the substatement of the innermost enclosing iteration statement.

The program is ill-formed if there is no such statement.

Recommended practice: The use of a fallthrough statement should suppress a warning that an implementation might otherwise issue for a case or default label that is reachable from another case or default label along some path of execution.

Implementations should issue a warning if a fallthrough statement is not dynamically reachable.

[Example 1: void f(int n) { void g(), h(), i();switch (n) { case 1: case 2: g();[[fallthrough]];case 3: do { [[fallthrough]]; } while (false);case 6: do { [[fallthrough]]; } while (n--);case 7: while (false) { [[fallthrough]]; } case 5: h();case 4: i();[[fallthrough]]; } } — _end example_]

9.12.6 Likelihood attributes [dcl.attr.likelihood]

The attribute-tokens likely and unlikelymay be applied to labels or statements.

The attribute-tokens likely and unlikelyshall appear at most once in each attribute-listand no attribute-argument-clause shall be present.

The attribute-token likelyshall not appear in an attribute-specifier-seqthat contains the attribute-token unlikely.

Recommended practice: The use of the likely attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more likely than any alternative path of execution that does not include such an attribute on a statement or label.

The use of the unlikely attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more unlikely than any alternative path of execution that does not include such an attribute on a statement or label.

A path of execution includes a label if and only if it contains a jump to that label.

[Note 1:

Excessive usage of either of these attributes is liable to result in performance degradation.

— _end note_]

[Example 1: void g(int);int f(int n) { if (n > 5) [[unlikely]] { g(0);return n * 2 + 1;} switch (n) { case 1: g(1);[[fallthrough]];[[likely]] case 2: g(2); break;} return 3;} — _end example_]

9.12.7 Maybe unused attribute [dcl.attr.unused]

The attribute-token maybe_­unusedindicates that a name or entity is possibly intentionally unused.

It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present.

The attribute may be applied to the declaration of a class, a typedef-name, a variable (including a structured binding declaration), a non-static data member, a function, an enumeration, or an enumerator.

A name or entity declared without the maybe_­unused attribute can later be redeclared with the attribute and vice versa.

An entity is considered marked after the first declaration that marks it.

Recommended practice: For an entity marked maybe_­unused, implementations should not emit a warning that the entity or its structured bindings (if any) are used or unused.

For a structured binding declaration not marked maybe_­unused, implementations should not emit such a warning unless all of its structured bindings are unused.

[Example 1: [[maybe_unused]] void f([[maybe_unused]] bool thing1,[[maybe_unused]] bool thing2) { [[maybe_unused]] bool b = thing1 && thing2; assert(b);}

Implementations should not warn that b is unused, whether or not NDEBUG is defined.

— _end example_]

9.12.8 Nodiscard attribute [dcl.attr.nodiscard]

The attribute-token nodiscardmay be applied to the declarator-idin a function declaration or to the declaration of a class or enumeration.

It shall appear at most once in each attribute-list.

An attribute-argument-clause may be present and, if present, shall have the form:

A name or entity declared without the nodiscard attribute can later be redeclared with the attribute and vice-versa.

[Note 1:

Thus, an entity initially declared without the attribute can be marked as nodiscardby a subsequent redeclaration.

However, after an entity is marked as nodiscard, later redeclarations do not remove the nodiscardfrom the entity.

— _end note_]

Redeclarations using different forms of the attribute (with or without the attribute-argument-clauseor with different attribute-argument-clauses) are allowed.

A nodiscard type is a (possibly cv-qualified) class or enumeration type marked nodiscard in a reachable declaration.

A nodiscard call is either

Recommended practice: Appearance of a nodiscard call as a potentially-evaluated discarded-value expression ([expr.prop]) is discouraged unless explicitly cast to void.

Implementations should issue a warning in such cases.

[Note 2:

This is typically because discarding the return value of a nodiscard call has surprising consequences.

— _end note_]

The string-literalin a nodiscard attribute-argument-clauseshould be used in the message of the warning as the rationale for why the result should not be discarded.

[Example 1: struct [[nodiscard]] my_scopeguard { };struct my_unique { my_unique() = default; [[nodiscard]] my_unique(int fd) { } ~my_unique() noexcept { } };struct [[nodiscard]] error_info { }; error_info enable_missile_safety_mode();void launch_missiles();void test_missiles() { my_scopeguard(); (void)my_scopeguard(), launch_missiles(); my_unique(42); my_unique(); enable_missile_safety_mode(); launch_missiles();}error_info &foo();void f() { foo(); } — _end example_]

9.12.9 Noreturn attribute [dcl.attr.noreturn]

The attribute-token noreturn specifies that a function does not return.

It shall appear at most once in each attribute-list and noattribute-argument-clause shall be present.

The attribute may be applied to thedeclarator-id in a function declaration.

The first declaration of a function shall specify the noreturn attribute if any declaration of that function specifies thenoreturn attribute.

If a function is declared with the noreturn attribute in one translation unit and the same function is declared without the noreturn attribute in another translation unit, the program is ill-formed, no diagnostic required.

If a function f is called where f was previously declared with the noreturnattribute and f eventually returns, the behavior is undefined.

[Note 1:

The function may terminate by throwing an exception.

— _end note_]

Recommended practice: Implementations should issue a warning if a function marked [[noreturn]] might return.

[Example 1: [[ noreturn ]] void f() { throw "error"; } [[ noreturn ]] void q(int i) { if (i > 0) throw "positive";} — _end example_]

9.12.10 No unique address attribute [dcl.attr.nouniqueaddr]

The attribute-token no_­unique_­addressspecifies that a non-static data member is a potentially-overlapping subobject ([intro.object]).

It shall appear at most once in each attribute-listand no attribute-argument-clause shall be present.

The attribute may appertain to a non-static data member other than a bit-field.

[Note 1:

The non-static data member can share the address of another non-static data member or that of a base class, and any padding that would normally be inserted at the end of the object can be reused as storage for other members.

— _end note_]

[Example 1: template<typename Key, typename Value,typename Hash, typename Pred, typename Allocator> class hash_map { [[no_unique_address]] Hash hasher;[[no_unique_address]] Pred pred;[[no_unique_address]] Allocator alloc; Bucket *buckets;public: };

Here, hasher, pred, and alloccould have the same address as bucketsif their respective types are all empty.

— _end example_]