9 Declarations [dcl.dcl]

9.1 Preamble [dcl.pre]

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