Declarations
Declarations are how names are introduced (or re-introduced) into the C++ program. Not all declarations actually declare anything, and each kind of entity is declared differently. Definitions are declarations that are sufficient to use the entity identified by the name.
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
|
(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:
| (since C++11) |
| (since C++20) | |
|
(since C++11) |
-
- simple declaration
Contents |
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
;
|
(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) | ||||||||
| 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
typedefspecifier. 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 .
|
(since C++17) |
-
the
friendspecifier, allowed in class and function declarations.
|
(since C++11) |
|
(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
| (since C++11) | |
| (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 )
-
|
(since C++17) |
-
-
- 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.
-
-
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 .
|
(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 const static const , or virtual inline virtual 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 operators 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
[
constant-expression
(optional)
]
attr
(optional)
|
(8) | ||||||||
noptr-declarator
(
parameter-list
)
cv
(optional)
ref
(optional)
except
(optional)
attr
(optional)
|
(9) | ||||||||
(
declarator
)
|
(10) | ||||||||
D
as a pointer to the type determined by
decl-specifier-seq
S
.
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
::
D
as an lvalue reference to the type determined by
decl-specifier-seq
S
.
D
as an rvalue reference to the type determined by
decl-specifier-seq
S
.
|
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.
|
This section is incomplete
Reason: explain declaration name hiding rules; how a variable/function declaration hides a class (but not a typedef) with the same name |
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
|