Declarations
Declarations introduce (or re-introduce) names into the C++ program. 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:
- 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:
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) |
|
attr |
- |
(since C++11) sequence of any number of attributes |
decl-specifier-seq |
- |
sequence of specifiers (see below) |
init-declarator-list |
- |
comma-separated list of declarators with optional initializers. init-declarator-list is optional when declaring a named class/struct/union or a named enumeration |
A structured binding declaration is also a simple declaration. (since C++17).
Specifiers
Declaration specifiers (decl-specifier-seq) is a sequence of the following whitespace-separated specifiers, in any order:
- 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
.
- 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
-
- elaborated type specifier
- the keyword
class
, struct
, or union
, followed by the identifier (optionally qualified), previously defined as the name of a class, struct, or union.
- 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 const static const
, or virtual inline virtual
are errors, except that long
is allowed to appear twice (since C++11).
Declarators
init-declarator-list is a comma-separated sequence of one or more init-declarators, which have the following syntax:
declarator initializer(optional) |
(1) |
|
declarator requires-clause |
(2) |
(since C++20) |
Each init-declarator in a init-declarator sequence 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 [ constexpr(optional) ] attr(optional) |
(8) |
|
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional) |
(9) |
|
1) The
name that is declared.
4)
Pointer declarator: the declaration
S * D;
declares
D
as a pointer to the type determined by
decl-specifier-seq
S
.
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.
Note that the outermost function declarator 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.
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.
Examples
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 http://cdecl.org.
#include <string>
class C {
std::string member; // decl-specifier-seq is "std::string"
// declarator is "member"
} obj, *pObj(&obj);
// decl-specifier-seq is "class C { std::string member; }"
// declarator "obj" defines an object of type C
// declarator "*pObj(&obj)" declares and initializes a pointer to C
int a = 1, *p = nullptr, f(), (*pf)(double);
// decl-specifier-seq is int
// declarator a = 1 defines and initializes a variable of type int
// declarator *p = nullptr defines and initializes a variable of type int*
// declarator (f)() declares (but doesn't define)
// a function taking no arguments and returning int
// declarator (*pf)(double) defines a pointer to function
// taking double and returning int
int (*(*foo)(double))[3] = nullptr;
// decl-specifier-seq is int
// 1. declarator "(*(*foo)(double))[3]" is an array declarator:
// the type declared is "/nested declarator/ array of 3 int"
// 2. the nested declarator is "(*(*foo)(double))", which is a pointer declarator
// the type declared is "/nested declarator/ pointer to array of 3 int"
// 3. the nested declarator is "(*foo)(double)", which is a function declarator
// the type declared is "/nested declarator/ function taking double and returning
// pointer to array of 3 int"
// 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by
// function declarator syntax) pointer declarator.
// the type declared is "/nested declarator/ pointer to function taking double
// and returning pointer to array of 3 int"
// 5. the nested declarator is "foo", which is an identifier.
// The declaration declares the object foo of type "pointer to function taking double
// and returning pointer to array of 3 int"
// The initializer "= nullptr" provides the initial value of this pointer.
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