Spec-Zone.ru › C++

Declarations

Declarations are how names and/or entities are introduced (or re-introduced) into the C++ program. Not all declarations actually declare anything, and each kind of entity is declared differently. A definition is a declaration that provides a complete definition for the introduced entity.

A declaration is one of the following:

  • Function definition
  • Template declaration (including Partial template specialization)
  • Explicit template instantiation
  • Explicit template specialization
  • Namespace definition
  • Linkage specification
  • Attribute declaration (attr ;)
(since C++11)
  • Empty declaration (;)
  • A function declaration without a decl-specifier-seq :
attr (optional) declarator ;
attr - (since C++11) sequence of any number of attributes
declarator - a function declarator
This declaration must declare a constructor, destructor, or user-defined type conversion function. It can only be used as part of a template declaration, explicit specialization, or explicit instantiation.
  • block-declaration (a declaration that can appear inside a block), which, in turn, can be one of the following:
    • asm declaration
  • type alias declaration
(since C++11)
  • namespace alias definition
  • using-declaration
  • using directive
  • using-enum-declaration
(since C++20)
  • static_assert declaration
  • opaque enum declaration
(since C++11)
  • simple declaration

Note that declaration is a syntactic construct, whereas declaration is a C++ term. A declaration can be not only a declaration but also a class specifier, an enum specifier, a function parameter, and so on. For a name, the declaration that introduces it and the declaration of the entity it denotes are not always the same declaration:

template <class T>
struct A {};

In this example, the declaration that introduces the name A is the class specifier, whereas the declaration that introduces the entity class template A is its enclosing template declaration.

Simple declaration

A simple declaration is a statement that introduces, creates, and optionally initializes one or several identifiers, typically variables.

decl-specifier-seq init-declarator-list (optional) ; (1)
attr decl-specifier-seq init-declarator-list (optional) ; (2) (since C++11)
decl-specifier-seq - sequence of specifiers
init-declarator-list - comma-separated list of init-declarator s (see below)
attr - sequence of any number of attributes


init-declarator-list can only be omitted when declaring a named class or a named enumeration.

A structured binding declaration is also a simple declaration.

(since C++17)


The syntax of init-declarator is defined as follows:

declarator initializer (1)
declarator requires-clause (optional) contract-specs (optional) (2)
1) A declarator with an initializer.
2) A declarator without an initializer.
declarator - a declarator
initializer - an initializer
requires-clause - (since C++20) a requires clause
contract-specs - (since C++26) a list of function contract specifiers

requires-clause can only appear if declarator declares a templated function.

(since C++20)

contract-specs can only appear if declarator declares a function or function template.

(since C++26)

Specifiers

Declaration specifiers (decl-specifier-seq) is a sequence of the following whitespace-separated specifiers, in any order:

  • the typedef specifier. If present, the entire declaration is a typedef declaration and each declarator introduces a new type name, not an object or a function.
  • function specifiers (inline, virtual, explicit), only allowed in function declarations.
  • the inline specifier is also allowed on variable declarations.
(since C++17)
  • the friend specifier, allowed in class and function declarations.
  • the constexpr specifier, only allowed in variable definitions, function and function template declarations, and the declaration of static data members of literal type.
(since C++11)
  • the consteval specifier, only allowed in function and function template declarations.
  • the constinit specifier, only allowed in declaration of a variable with static or thread storage duration. At most one of the constexpr, consteval, and constinit specifiers is allowed to appear in a decl-specifier-seq.
(since C++20)
  • storage class specifier (register, (until C++17) static, thread_local, (since C++11) extern, mutable). Only one storage class specifier is allowed, except that thread_local may appear together with extern or static(since C++11).
  • Type specifiers (type-specifier-seq), a sequence of specifiers that names a type. The type of every entity introduced by the declaration is this type, optionally modified by the declarator (see below). This sequence of specifiers is also used by type-id. Only the following specifiers are part of type-specifier-seq, in any order:
  • class specifier
  • enum specifier
  • simple type specifier
    • char, char8_t, (since C++20) char16_t, char32_t, (since C++11) wchar_t, bool, short, int, long, signed, unsigned, float, double, void
  • auto
  • decltype specifier
(since C++11)
  • pack indexing specifier
  • splice type specifier
(since C++26)
  • previously declared class name (optionally qualified)
  • previously declared enum name (optionally qualified)
  • previously declared typedef-name or type alias(since C++11) (optionally qualified)
  • template name with template arguments (optionally qualified, optionally using template disambiguator)
  • template name without template arguments (optionally qualified): see class template argument deduction
(since C++17)
  • elaborated type specifier
    • the keyword class, struct, or union, followed by the identifier (optionally qualified), previously defined as the name of a class.
    • the keyword class, struct, or union, followed by template name with template arguments (optionally qualified, optionally using template disambiguator), previously defined as the name of a class template.
    • the keyword enum followed by the identifier (optionally qualified), previously declared as the name of an enumeration.
  • typename specifier
  • cv qualifier
only one type specifier is allowed in a decl-specifier-seq, with the following exceptions:
  • 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.
(since C++11)

Attributes may appear in decl-specifier-seq, in which case they apply to the type determined by the preceding specifiers.

Repetitions of any specifier in a decl-specifier-seq, such as conststaticconst, or virtualinlinevirtual are errors, except that long is allowed to appear twice(since C++11).

Declarators

Each init-declarator in an init-declarator-list S D1, D2, D3; is processed as if it were a standalone declaration with the same specifiers: S D1; S D2; S D3;.

Each declarator introduces exactly one object, reference, function, or (for typedef declarations) type alias, whose type is provided by decl-specifier-seq and optionally modified by symbols such as & (reference to) or [] (array of) or () (function returning) in the declarator. These operators can be applied recursively, as shown below.

A declarator is one of the following:

unqualified-id attr (optional) (1)
qualified-id attr (optional) (2)
... identifier attr (optional) (3) (since C++11)
* attr (optional) cv (optional) declarator (4)
nested-name-specifier * attr (optional) cv (optional) declarator (5)
& attr (optional) declarator (6)
&& attr (optional) declarator (7) (since C++11)
noptr-declarator [ constexpr (optional) ] attr (optional) (8)
noptr-declarator ( parameter-list ) cv (optional) ref  (optional) except (optional) attr (optional) (9)
1) The name that is declared.
2) A declarator that uses a qualified identifier (qualified-id) defines or redeclares a previously declared namespace member or class member. If it appears in a non-friend declaration, this declaration must be in namespace scope.
3) Parameter pack, only appears in parameter declarations.
4) Pointer declarator: the declaration S * D; declares D as a pointer to the type determined by decl-specifier-seq S.
5) Pointer to member declaration: the declaration S C::* D; declares D as a pointer to member of C of type determined by decl-specifier-seq S. nested-name-specifier is a sequence of names and scope resolution operators ::
6) Lvalue reference declarator: the declaration S & D; declares D as an lvalue reference to the type determined by decl-specifier-seq S.
7) Rvalue reference declarator: the declaration S && D; declares D as an rvalue reference to the type determined by decl-specifier-seq S.
8) Array declarator. noptr-declarator any valid declarator, but if it begins with *, &, or &&, it has to be surrounded by parentheses.
9) Function declarator. noptr-declarator any valid declarator, but if it begins with *, &, or &&, it has to be surrounded by parentheses. It may end with the optional trailing return type.(since C++11)

In all cases, attr is an optional sequence of attributes. When appearing immediately after the identifier, it applies to the object being declared.

(since C++11)

cv is a sequence of const and volatile qualifiers, where either qualifier may appear at most once in the sequence.

Due to the name lookup rules, when declarations of a type and another entity with the same name appear in the same scope, the type name is hidden. Note that the type can still be found via an elaborated type specifier.

class Foo {};
void Foo();
// Foo obj;// Error: 'Foo' is not a type
class Foo obj;// OK

Notes

When a block-declaration appears inside a block, and an identifier introduced by a declaration was previously declared in an outer block, the outer declaration is hidden for the remainder of the block.

If a declaration introduces a variable with automatic storage duration, it is initialized when its declaration statement is executed. All automatic variables declared in a block are destroyed on exit from the block (regardless how the block is exited: via exception, goto, or by reaching its end), in order opposite to their order of initialization.

Example

Note: this example demonstrates how some complex declarations are parsed in terms of the language grammar. Other popular mnemonics are: the spiral rule, reading inside-out, and declaration mirrors use. There is also an automated parser at https://cdecl.org.

#include <type_traits>

struct S
{
    int member;
    // decl-specifier-seq is "int"
    // declarator is "member"
} obj, *pObj(&obj);
// decl-specifier-seq is "struct S { int member; }"
// declarator "obj" declares an object of type S
// declarator "*pObj" declares a pointer to S,
//     and initializer "(&obj)" initializes it

int i = 1, *p = nullptr, f(), (*pf)(double);
// decl-specifier-seq is "int"
// declarator "i" declares a variable of type int,
//     and initializer "= 1" initializes it
// declarator "*p" declares a variable of type int*,
//     and initializer "= nullptr" initializes it
// declarator "f()" declares (but doesn't define)
//     a function taking no arguments and returning int
// declarator "(*pf)(double)" declares a pointer to function
//     taking double and returning int

int (*(*var1)(double))[3] = nullptr;
// decl-specifier-seq is "int"
// declarator is "(*(*var1)(double))[3]"
// initializer is "= nullptr"

// 1. declarator "(*(*var1)(double))[3]" is an array declarator:
//    Type declared is: "(*(*var1)(double))" array of 3 elements
// 2. declarator "(*(*var1)(double))" is a pointer declarator:
//    Type declared is: "(*var1)(double)" pointer to array of 3 elements
// 3. declarator "(*var1)(double)" is a function declarator:
//    Type declared is: "(*var1)" function taking "(double)",
//    returning pointer to array of 3 elements.
// 4. declarator "(*var1)" is a pointer declarator:
//    Type declared is: "var1" pointer to function taking "(double)",
//    returning pointer to array of 3 elements.
// 5. declarator "var1" is an identifier.
// This declaration declares the object var1 of type "pointer to function
// taking double and returning pointer to array of 3 elements of type int"
// The initializer "= nullptr" provides the initial value of this pointer.

// C++11 alternative syntax:
auto (*var2)(double) -> int (*)[3] = nullptr;
// decl-specifier-seq is "auto"
// declarator is "(*var2)(double) -> int (*)[3]"
// initializer is "= nullptr"

// 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator:
//    Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]"
// ...

int main()
{
    static_assert(std::is_same_v<decltype(var1), decltype(var2)>);
}

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 482 C++98 the declarators of redeclarations could not be qualified qualified declarators allowed
CWG 569 C++98 a single standalone semicolon was not a valid declaration it is an empty declaration,
which has no effect
CWG 1830 C++98 repetition of a function specifier in a decl-specifier-seq was allowed repetition is forbidden

See also

C documentation for Declarations

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/declarations

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API