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 Clause [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 a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class or enumeration, that is, when the decl-specifier-seq contains either a class-specifier, an elaborated-type-specifier with a class-key ([class.name]), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (as class-names, enum-names, or enumerators, 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:
enum { }; typedef class { };
— end example ]
Syntactic components beyond those found in the general form of declaration are added to a function declaration to make a function-definition. An object declaration, however, is also a definition unless it contains the extern specifier and has no initializer ([basic.def]). A definition causes the appropriate amount of storage to be reserved and any appropriate initialization ([dcl.init]) to be done.
10.1 Specifiers [dcl.spec]If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous defining-type-specifier other than a cv-qualifier in the decl-specifier-seq. The sequence shall be self-consistent as described below. [ Example:
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 than const or volatile) has to be present to indicate that the typedef-name Pc is the name being (re)declared, rather than being part of the decl-specifier sequence. For another example,
void f(const Pc); void g(const int Pc);
— end example ]
[ Note: Since signed, unsigned, long, and short by default imply int, a type-name appearing after one of those specifiers is treated as the name being (re)declared. [ Example:
void h(unsigned Pc); void k(unsigned int Pc);
— end example ] — end note ]
10.1.1 Storage class specifiers [dcl.stc]The static specifier can be applied only to names of variables and functions and to anonymous unions. There can be no static function declarations within a block, nor any static function parameters. A static specifier used in the declaration of a variable declares the variable to have static storage duration, unless accompanied by the thread_local specifier, which declares the variable to have thread 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 can be applied only to the names of variables and functions. The extern specifier cannot be used in the declaration of class members or function parameters. For the linkage of a name declared with an extern specifier, see [basic.link]. [ Note: The extern keyword can also be used in explicit-instantiations and linkage-specifications, but it is not a storage-class-specifier in such contexts. — end note ]
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. Each function in a given set of overloaded functions can have a different linkage, however. [ Example:
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 an extern declaration. Such a declaration can only be used in ways that do not require a complete class type. [ Example:
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 member whose type is neither const-qualified nor a reference type. [ Example:
class X { mutable const int* p; mutable int* const q; };
— end example ]
The mutable specifier on a class data member nullifies a const specifier applied to the containing class object and permits modification of the mutable class member even though the rest of the object is const ([dcl.type.cv]).
10.1.2 Function specifiers [dcl.fct.spec]The virtual specifier shall be used only in the initial declaration of a non-static class member function; see [class.virtual].
The explicit specifier shall be used only in the declaration of a constructor or conversion function within its class definition; see [class.conv.ctor] and [class.conv.fct].
10.1.3 The typedef specifier [dcl.typedef]In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [ Example:
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 redefine 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:
struct S { typedef struct A { } A; typedef struct B B; typedef A A; };
— end example ]
If a typedef specifier is used to redefine in a given scope an entity that can be referenced using an elaborated-type-specifier, the entity can continue to be referenced by an elaborated-type-specifier or as an enumeration or class name in an enumeration or class definition respectively. [ Example:
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 redefine the name of any type declared in that scope to refer to a different type. [ Example:
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:
typedef int complex; class complex { /* ... */ };
— end example ]
If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only ([basic.link]). [ Example:
typedef struct { } *ps, S;
— end example ]
10.1.5 The constexpr specifier [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. A function or static data member declared with the constexpr specifier is implicitly an inline function or variable ([dcl.inline]). If any declaration of a function or function template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. — end note ] [ Note: Function parameters cannot be declared constexpr. — end note ] [ Example:
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 specifier used in the declaration of a function that is not a constructor declares that function to be a constexpr function. Similarly, a constexpr specifier used in a constructor declaration declares that constructor to be a constexpr constructor.
The definition of a constexpr function shall satisfy the following requirements:
it shall not be virtual;
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
an identifier label,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
[ Example:
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() { int a; return 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 shall satisfy the following requirements:
the class shall not have any virtual base classes;
each of the parameter types shall be a literal type;
its function-body shall not be a function-try-block.
In addition, either its function-body shall be = delete, or it shall satisfy the following requirements:
either its function-body shall be = default, or the compound-statement of its function-body shall satisfy the requirements for a function-body of a constexpr function;
every non-variant non-static data member and base class subobject shall be initialized ([class.base.init]);
if the class is a union having variant members ([class.union]), exactly one of them shall be initialized;
if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
for a delegating constructor, the target constructor shall be a constexpr constructor.
[ Example:
struct Length { constexpr explicit Length(int i = 0) : val(i) { } private: int val; };
— end example ]
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 a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required. [ Example:
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 or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, 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 or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed, no diagnostic required.
A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that
a call to a constexpr function can appear in a constant expression and
copy elision is mandatory in a constant expression ([class.copy]).
The constexpr specifier has no effect on the type of a constexpr function or a constexpr constructor. [ Example:
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. [ Example:
struct pixel { int x, y; }; constexpr pixel ur = { 1294, 1024 }; constexpr pixel origin;
— end example ]
10.1.6 The inline specifier [dcl.inline]The inline specifier can be applied only to the declaration or definition of a variable or function.
A function declaration ([dcl.fct], [class.mfct], [class.friend]) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions specified in this section shall still be respected.
A variable declaration with an inline specifier declares an inline variable.
A function defined within a class definition is an inline function.
The inline specifier shall not appear on a block scope declaration.94 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.
An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]). [ Note: A call to the inline function or a use of the inline variable may be encountered before its definition appears in the translation unit. — end note ] If the definition of a function or variable appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function or variable with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function or variable with external linkage shall have the same address in all translation units. [ Note: A static local variable in an inline function with external linkage always refers to the same object. A type defined within the body of an inline function with external linkage is the same type in every translation unit. — end note ]
10.1.7 Type specifiers [dcl.type]As a general rule, at most one defining-type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a defining-type-specifier-seq, and at most one type-specifier is allowed in a type-specifier-seq. The only exceptions to this rule are the following:
const can be combined with any type specifier except itself.
volatile can be combined with any type specifier except itself.
signed or unsigned can be combined with char, long, short, or int.
short or long can be combined with int.
long can be combined with double.
long can be combined with long.
[ Note: 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: Cv-qualifiers are supported by the type system so that they cannot be subverted without casting. — end note ]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior. [ Example:
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 are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.
[ Note: 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. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]
10.1.7.2 Simple type specifiers [dcl.type.simple]The simple-type-specifier auto is a placeholder for a type to be deduced ([dcl.spec.auto]). A type-specifier of the form typenameopt nested-name-specifieropt template-name is a placeholder for a deduced class type ([dcl.type.class.deduct]). The template-name shall name a class template that is not an injected-class-name. The other simple-type-specifiers specify either a previously-declared type, a type determined from an expression, or one of the fundamental types. Table 11 summarizes the valid combinations of simple-type-specifiers and the types they specify.
Table
11—
simple-type-specifiersand the types they specify
When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [ Note: 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 ]
For an expression e, the type denoted by decltype(e) is defined as follows:
if e is an unparenthesized id-expression naming a structured binding ([dcl.struct.bind]), decltype(e) is the referenced type as given in the specification of the structured binding declaration;
otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
otherwise, decltype(e) is the type of e.
The operand of the decltype specifier is an unevaluated operand.
[ Example:
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: 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, the temporary materialization conversion is not applied and no result object is provided for the prvalue. The type of the prvalue may be incomplete. [ Note: As a result, storage is not allocated for the prvalue and it is not destroyed. Thus, a class type is not instantiated as a result of being the type of a function call in this context. In this context, the common purpose of writing the expression is merely to refer to its type. In that sense, a decltype-specifier is analogous to a use of a typedef-name, so the usual reasons for requiring a complete type do not apply. In particular, it is not necessary to allocate storage for a temporary object or to enforce the semantic constraints associated with invoking the type's destructor. — end note ] [ Note: Unlike the preceding rule, parentheses have no special meaning in this context. — end note ] [ Example:
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 ]
10.1.7.4 The auto specifier [dcl.spec.auto]A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
If the init-declarator-list contains more than one init-declarator, they shall all form declarations of variables. The type of each declared variable is determined by placeholder type deduction, and if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed.
[ Example:
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 a return statement with no operand at the closing brace of the function body. [ Example:
auto f() { } auto* g() { }
— end example ]
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements. [ Example:
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 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: 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:
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. [ Example:
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; }
— end example ]
A function declared with a return type that uses a placeholder type shall not be virtual.
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:
template <typename T> auto f(T t) { return t; } extern template auto f(int); int (*p)(int) = f;
— end example ]
10.1.7.4.1 Placeholder type deduction [dcl.type.auto.deduct]A type T containing a placeholder type, and a corresponding initializer e, are determined as follows:
for a non-discarded return statement that occurs in a function declared with a return type that contains a placeholder type, T is the declared return type and e is the operand of the return statement. If the return statement has no operand, then e is void();
for a variable declared with a type that contains a placeholder type, T is the declared type of the variable and e is the initializer. If the initialization is direct-list-initialization, the initializer shall be a braced-init-list containing only a single assignment-expression and e is the assignment-expression;
for a non-type template parameter declared with a type that contains a placeholder type, T is the declared type of the non-type template parameter and e is the corresponding template argument.
In the case of a return statement with no operand or with an operand of type void, T shall be either decltype(auto) or cv auto.
If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initialization is copy-list-initialization, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is e. If the deduction fails, the declaration is ill-formed. Otherwise, T' is obtained by substituting the deduced U into P. [ Example:
auto x1 = { 1, 2 }; auto x2 = { 1, 2.0 }; auto x3{ 1, 2 }; auto x4 = { 3 }; auto x5{ 3 };
— end example ]
[ Example:
const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:
template <class U> void f(const U& u);
— end example ]
If the placeholder is the decltype(auto) type-specifier, T shall be the placeholder alone. The type deduced for T is determined as described in [dcl.type.simple], as though e had been the operand of the decltype. [ Example:
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 ]
10.1.7.5 Deduced class template specialization types [dcl.type.class.deduct][ Example:
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 ]
10.2 Enumeration declarations [dcl.enum]The enumeration type declared with an enum-key of only enum is an unscoped enumeration, and its enumerators are unscoped enumerators. The enum-keys enum class and enum struct are semantically equivalent; an enumeration type declared with one of these is a scoped enumeration, and its enumerators are scoped enumerators. The optional identifier shall not be omitted in the declaration of a scoped enumeration. The type-specifier-seq of an enum-base shall 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. An enumerator-definition with = gives the associated enumerator the value indicated by the constant-expression. If the first enumerator has no initializer, the value of the corresponding constant is zero. An enumerator-definition without an initializer gives the enumerator the value obtained by increasing the value of the previous enumerator by one. [ Example:
enum { a, b, c=0 }; enum { d, e, f=e+2 };
defines a, c, and d to be zero, b and e to be 1, and f to be 3. — end example ] The optional attribute-specifier-seq in an enumerator 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: An enumeration declared by an opaque-enum-declaration has 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.
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 be fixed. 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-definition shall be a converted constant expression of the underlying type. If the underlying type is not fixed, the type of each enumerator prior to the closing brace is determined as follows:
If an initializer is specified for an enumerator, the constant-expression shall be an integral constant expression. If the expression has unscoped enumeration type, the enumerator has the underlying type of that enumeration type, otherwise it has the same type as the expression.
If no initializer is specified for the first enumerator, its type is an unspecified signed integral type.
Otherwise the type of the enumerator is the same as that of the preceding enumerator unless the incremented value is not representable in that type, in which case the type is an unspecified integral type sufficient to contain the incremented value. If no such type exists, the program is ill-formed.
An enumeration whose underlying type is fixed is an incomplete type from its point of declaration to immediately after its enum-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 its enum-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 int unless the value of an enumerator cannot fit in an int or unsigned 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, for an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a ones' complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|−K,|emax|) and equal to 2M−1, where M is a non-negative integer. bmin is zero if emin is non-negative and −(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise. 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.96
The value of an enumerator or an object of an unscoped enumeration type is converted to an integer by integral promotion. [ Example:
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 declares col as an object of that type, and cp as a pointer to an object of that type. The possible values of an object of type color are red, yellow, green, blue; these values can be converted to the integral values 0, 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 int conversion 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. These names obey the scope rules defined for all names in [basic.scope] and [basic.lookup]. [ Example:
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 ] An enumerator declared in class scope can be referred to using the class member access operators (::, . (dot) and -> (arrow)), see [expr.ref]. [ Example:
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 ]
10.3 Namespaces [basic.namespace]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.
10.3.1 Namespace definition [namespace.def]namespace-name: identifier namespace-alias
namespace-definition: named-namespace-definition unnamed-namespace-definition nested-namespace-definition
named-namespace-definition: inlineopt namespace attribute-specifier-seqopt identifier { namespace-body }
unnamed-namespace-definition: inlineopt namespace attribute-specifier-seqopt { namespace-body }
nested-namespace-definition: namespace enclosing-namespace-specifier :: identifier { namespace-body }
enclosing-namespace-specifier: identifier enclosing-namespace-specifier :: identifier
namespace-body: declaration-seqopt
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:
namespace Q { namespace V { void f(); class C { void m(); }; } void V::f() { extern void h(); } void V::C::m() { } }
— end example ]
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 in argument-dependent lookup whenever one of them is, and a using-directive that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace. Furthermore, each member of the inline namespace can subsequently be partially specialized, explicitly instantiated, or explicitly specialized as though it were a member of the enclosing 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 namespace M, which in turn contains an inline namespace O, then the members of O 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.
10.3.1.1 Unnamed namespaces [namespace.unnamed]An unnamed-namespace-definition behaves as if it were replaced by
inlineopt namespace unique { /* empty body */ } using namespace unique ; namespace unique { namespace-body }
where inline appears if and only if it appears in the unnamed-namespace-definition and 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-seq in the unnamed-namespace-definition appertains to unique. [ Example:
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 ]
10.3.1.2 Namespace member definitions [namespace.memdef]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:
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 template97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup ([basic.lookup.unqual]) or qualified lookup ([basic.lookup.qual]). [ Note: 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 a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace. [ Note: 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:
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 ]
10.3.2 Namespace alias [namespace.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: 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 ]
10.3.3 The using declaration [namespace.udecl]using-declaration: using using-declarator-list ;
using-declarator-list: using-declarator ...opt using-declarator-list , using-declarator ...opt
using-declarator: typenameopt nested-name-specifier unqualified-id
Every using-declaration is a declaration and a member-declaration and can therefore be used in a class definition. [ Example:
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 ]
[ Note: Since destructors do not have names, a using-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]), 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.
A using-declaration shall not name a template-id. [ Example:
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 that names a class member shall be a member-declaration. [ Example:
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:
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:
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: 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 the using-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 same declarative region also apply to using-declarations. [ Example:
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, return type, and template parameter list as a function template introduced by a using-declaration, the program is ill-formed. [ Note: Two using-declarations may 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:
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, cv-qualification, and ref-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:
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 ]
For the purpose of 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. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class. Likewise, constructors that are introduced by a using-declaration are 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]). 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]).
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 a using-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: 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:
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:
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 ]
10.3.4 Using directive [namespace.udir]using-directive: attribute-specifier-seqopt using namespace nested-name-specifieropt namespace-name ;
A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: 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:
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, the using-directive is transitive: if a scope contains a using-directive that nominates a second namespace that itself contains using-directives, the effect is as if the using-directives from the second namespace also appeared in the first. [ Note: For qualified lookup, see [namespace.qual]. — end note ] [ Example:
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 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, the use of the name is ill-formed. [ Note: 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: In particular, the order in which namespaces were considered and the relationships among the namespaces implied by the using-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 through using-directives in the namespace of the other.99 [ Example:
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 ]
10.5 Linkage specifications [dcl.link]All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: 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 may 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.
Every implementation shall provide for linkage to functions written in the C programming language, "C", and linkage to C++ functions, "C++". [ Example:
complex sqrt(complex); extern "C" { double sqrt(double); }
— end example ]
Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope. In a linkage-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:
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:
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 and parameter-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: Only one definition for an entity with a given name with C language linkage may 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:
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 a linkage-specification is treated as if it contains the extern specifier 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:
extern "C" double f(); static double f(); extern "C" int i; extern "C" { int i; } extern "C" static void g();
— end example ]
[ Note: 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.
10.6 Attributes [dcl.attr] 10.6.1 Attribute syntax and semantics [dcl.attr.grammar][ Note: For each individual attribute, the form of the balanced-token-seq will be specified. — end note ]
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: 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:
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 ]
10.6.2 Alignment specifier [dcl.align]When the alignment-specifier is of the form alignas( constant-expression ):
the constant-expression shall be an integral constant expression
if the constant expression does not evaluate to an alignment value ([basic.align]), or evaluates to an extended alignment and the implementation does not support that alignment in the context of the declaration, the program is ill-formed.
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-specifiers appertaining to that entity were omitted. [ Example:
struct alignas(8) S {}; struct alignas(1) U { S s; };
— end example ]
If the defining declaration of an entity has an alignment-specifier, any non-defining declaration of that entity shall either specify equivalent alignment or have no alignment-specifier. Conversely, if any declaration of an entity has an alignment-specifier, every defining declaration of that entity shall specify an equivalent alignment. No diagnostic is required if declarations of an entity have different alignment-specifiers in different translation units. [ Example:
struct S { int x; } s, *p = &s; struct alignas(16) S; extern S* p;
— end example ]
[ Example: An aligned buffer with an alignment requirement of A and holding N elements of type T can 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:
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 ]
10.6.3 Carries dependency attribute [dcl.attr.depend]The first declaration of a function shall specify the carries_dependency attribute for its declarator-id if any declaration of the function specifies the carries_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 the carries_dependency attribute in its first declaration in another translation unit, the program is ill-formed, no diagnostic required.
[ Note: The carries_dependency attribute does not change the meaning of the program, but may result in generation of more efficient code. — end note ]
[ Example:
/* Translation unit A. */ 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]); } /* Translation unit B. */ [[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 of f 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 to g carries a dependency into g, but its second call does not. The implementation might need to insert a fence prior to the second call to g.
— end example ]
10.6.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: 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. An attribute-argument-clause may be present and, if present, it shall have the form:
( string-literal )
[ Note: The string-literal in the attribute-argument-clause could 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: 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 the attribute-argument-clause or with different attribute-argument-clauses) are allowed.
[ Note: Implementations may 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 may include the text provided within the attribute-argument-clause of any deprecated attribute applied to the name or entity. — end note ]
10.6.5 Fallthrough attribute [dcl.attr.fallthrough]The attribute-token fallthrough may be applied to a null statement; such a statement is a fallthrough statement. The attribute-token fallthrough shall 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. The program is ill-formed if there is no such statement.
[ Note: The use of a fallthrough statement is intended to 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 are encouraged to issue a warning if a fallthrough statement is not dynamically reachable. — end note ]
[ Example:
void f(int n) { void g(), h(), i(); switch (n) { case 1: case 2: g(); [[fallthrough]]; case 3: h(); case 4: i(); [[fallthrough]]; } }
— end example ]
10.6.6 Maybe unused attribute [dcl.attr.unused]The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, an enumeration, or an enumerator.
[ Note: For an entity marked maybe_unused, implementations are encouraged not to emit a warning that the entity is unused, or that the entity is used despite the presence of the attribute. — end note ]
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.
[ Example:
[[maybe_unused]] void f([[maybe_unused]] bool thing1, [[maybe_unused]] bool thing2) { [[maybe_unused]] bool b = thing1 && thing2; assert(b); }
Implementations are encouraged not to warn that b is unused, whether or not NDEBUG is defined. — end example ]
10.6.7 Nodiscard attribute [dcl.attr.nodiscard][ Note: A nodiscard call is a function call expression that calls a function previously declared nodiscard, or whose return type is a possibly cv-qualified class or enumeration type marked nodiscard. Appearance of a nodiscard call as a potentially-evaluated discarded-value expression (Clause [expr]) is discouraged unless explicitly cast to void. Implementations are encouraged to issue a warning in such cases. This is typically because discarding the return value of a nodiscard call has surprising consequences. — end note ]
[ Example:
struct [[nodiscard]] error_info { /* ... */ }; error_info enable_missile_safety_mode(); void launch_missiles(); void test_missiles() { enable_missile_safety_mode(); launch_missiles(); } error_info &foo(); void f() { foo(); }
— end example ]
10.6.8 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 no attribute-argument-clause shall be present. The attribute may be applied to the declarator-id in a function declaration. The first declaration of a function shall specify the noreturn attribute if any declaration of that function specifies the noreturn 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 noreturn attribute and f eventually returns, the behavior is undefined. [ Note: The function may terminate by throwing an exception. — end note ] [ Note: Implementations are encouraged to issue a warning if a function marked [[noreturn]] might return. — end note ]
[ Example:
[[ noreturn ]] void f() { throw "error"; } [[ noreturn ]] void q(int i) { if (i > 0) throw "positive"; }
— end example ]
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4