7 Declarations [dcl.dcl]

Declarations generally specify how names are to be interpreted. Declarations have the form

    declaration-seq declaration
    using identifier attribute-specifier-seqopt = type-id ;
    decl-specifier-seqopt init-declarator-listopt ;
    attribute-specifier-seq decl-specifier-seqopt init-declarator-list ;
  static_assert ( constant-expression , string-literal ) ;
    attribute-specifier-seq ;

Note: asm-definitions are described in [dcl.asm], and linkage-specifications are described in [dcl.link]. Function-definitions are described in [dcl.fct.def] and template-declarations are described in Clause [temp]. Namespace-definitions are described in [namespace.def], using-declarations are described in [namespace.udecl] and using-directives are described in [namespace.udir].  — end note ]

The simple-declaration

attribute-specifier-seqopt decl-specifier-seqopt init-declarator-listopt ;

is divided into three parts. 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 Clause [dcl.decl]. The attribute-specifier-seq in a simple-declaration appertains to each of the entities declared by the declarators of the init-declarator-list. [ Note: In the declaration for an entity, attributes appertaining to that entity may appear at the start of the declaration and after the declarator-id for that declaration.  — end note ] [ Example:

[[noreturn]] void f [[noreturn]] (); // OK

 — end example ]

Except where otherwise specified, the meaning of an attribute-declaration is implementation-defined.

A declaration occurs in a scope ([basic.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 (Clause [class]) 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 among the names being declared by the declaration (as class-names, enum-names, or enumerators, depending on the syntax). In such cases, and except for the declaration of an unnamed bit-field ([class.bit]), 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 { };           // ill-formed
typedef class { };  //  ill-formed

 — end example ]

In a static_assert-declaration the constant-expression shall be a constant expression ([expr.const]) that can be contextually converted to bool (Clause [conv]). If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message ([intro.compliance]) shall include the text of the string-literal, except that characters not in the basic source character set ([lex.charset]) are not required to appear in the diagnostic message. [ Example:

static_assert(sizeof(long) >= 8, "64-bit code generation required for this library.");

 — end example ]

An empty-declaration has no effect.

Each init-declarator in the init-declarator-list contains exactly one declarator-id, which is the name declared by that init-declarator and hence one of the names declared by the declaration. The type-specifiers ([dcl.type]) in the decl-specifier-seq and the recursive declarator structure of the init-declarator describe a type ([dcl.meaning]), which is then associated with the name being declared by the init-declarator.

If the decl-specifier-seq contains the typedef specifier, the declaration is called a typedef declaration and the name of each init-declarator is declared to be a typedef-name, synonymous with its associated type ([dcl.typedef]). If the decl-specifier-seq contains no typedef specifier, the declaration is called a function declaration if the type associated with the name is a function type ([dcl.fct]) and an object declaration otherwise.

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.

Only in function declarations for constructors, destructors, and type conversions can the decl-specifier-seq be omitted.89

The “implicit int” rule of C is no longer supported.

7.1 Specifiers [dcl.spec]

The specifiers that can be used in a declaration are

    decl-specifier attribute-specifier-seqopt
    decl-specifier decl-specifier-seq

The optional attribute-specifier-seq in a decl-specifier-seq appertains to the type determined by the preceding decl-specifiers ([dcl.meaning]). The attribute-specifier-seq affects the type only for the declaration it appears in, not other declarations involving the same type.

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 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;                      // error: name missing

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 f(char* const) (not const char*)
void g(const int Pc);           // void g(const int)

 — 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 h(unsigned int)
void k(unsigned int Pc);        // void k(unsigned int)

 — end example ]  — end note ]

7.1.1 Storage class specifiers [dcl.stc]

The storage class specifiers are


At most one storage-class-specifier shall appear in a given decl-specifier-seq, except that thread_local may appear with static or extern. If thread_local appears in any declaration of a variable it shall be present in all declarations of that entity. If a storage-class-specifier appears in a decl-specifier-seq, there can be no typedef specifier in the same decl-specifier-seq and the init-declarator-list of the declaration shall not be empty (except for an anonymous union declared in a named namespace or in the global namespace, which shall be declared static ([class.union])). The storage-class-specifier applies to the name declared by each init-declarator in the list and not to any names declared by other specifiers. A storage-class-specifier shall not be specified in an explicit specialization ([temp.expl.spec]) or an explicit instantiation ([temp.explicit]) directive.

The register specifier shall be applied only to names of variables declared in a block ([stmt.block]) or to function parameters ([dcl.fct.def]). It specifies that the named variable has automatic storage duration ([basic.stc.auto]). A variable declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default.

A register specifier is a hint to the implementation that the variable so declared will be heavily used. [ Note: The hint can be ignored and in most implementations it will be ignored if the address of the variable is taken. This use is deprecated (see [depr.register]).  — end note ]

The thread_local specifier indicates that the named entity has thread storage duration ([basic.stc.thread]). It shall be applied only to the names of variables of namespace or block scope and to the names of static data members. When thread_local is applied to a variable of block scope the storage-class-specifier static is implied if it does not appear explicitly.

The static specifier can be applied only to names of variables and functions and to anonymous unions ([class.union]). 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 ([basic.stc.static]), unless accompanied by the thread_local specifier, which declares the variable to have thread storage duration ([basic.stc.thread]). 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 ]

A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.

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();               // f() has internal linkage
char* f()                       // f() still has internal linkage
  { /* ... */ }

char* g();                      // g() has external linkage
static char* g()                // error: inconsistent linkage
  { /* ... */ }

void h();
inline void h();                // external linkage

inline void l();
void l();                       // external linkage

inline void m();
extern void m();                // external linkage

static void n();
inline void n();                // internal linkage

static int a;                   // a has internal linkage
int a;                          // error: two definitions

static int b;                   // b has internal linkage
extern int b;                   // b still has internal linkage

int c;                          // c has external linkage
static int c;                   // error: inconsistent linkage

extern int d;                   // d has external linkage
static int d;                   // error: inconsistent linkage

 — 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);                         // error: S is incomplete
  f();                          // error: S is incomplete

 — end example ]

The mutable specifier can be applied only to names of class data members ([class.mem]) and cannot be applied to names declared const or static, and cannot be applied to reference members. [ Example:

class X {
  mutable const int* p;         // OK
  mutable int* const q;         // ill-formed

 — 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]).

7.1.2 Function specifiers [dcl.fct.spec]

Function-specifiers can be used only in function declarations.


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 defined by [dcl.fct.spec] shall still be respected.

A function defined within a class definition is an inline function. The inline specifier shall not appear on a block scope function declaration.90 If the inline specifier is used in a friend declaration, that declaration shall be a definition or the function shall have previously been declared inline.

An inline function 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 may be encountered before its definition appears in the translation unit.  — end note ] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function 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 with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [ Note: A string literal appearing in a default argument is not in the body of an inline function merely because the expression is used in a function call from that inline function.  — end note ] A type defined within the body of an extern inline function is the same type in every translation unit.

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].

The inline keyword has no effect on the linkage of a function.

7.1.3 The typedef specifier [dcl.typedef]

Declarations containing the decl-specifier typedef declare identifiers that can be used later for naming fundamental ([basic.fundamental]) or compound ([basic.compound]) types. The typedef specifier shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier, and it shall not be used in the decl-specifier-seq of a parameter-declaration ([dcl.fct]) nor in the decl-specifier-seq of a function-definition ([dcl.fct.def]).


A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause [dcl.decl]. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type the way a class declaration ([class.name]) or enum declaration does. [ Example: after

typedef int MILES, *KLICKSP;

the constructions

MILES distance;
extern KLICKSP metricp;

are all correct declarations; the type of distance is int and that of metricp is “pointer to int.”  — end example ]

A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id. [ Example:

using handler_t = void (*)(int);
extern handler_t ignore;
extern void (*ignore)(int);         // redeclare ignore
using cell = pair<void*, cell*>;    // ill-formed

 — end example ]

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;       // OK
  typedef struct B B;           // OK
  typedef A A;                  // error

 — 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;                  // OK
struct S { };                   // OK

 — 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;            // error: redefinition

 — 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 { /* ... */ };   // error: redefinition

 — end example ]

Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name ([class.name]). If a typedef-name is used to identify the subject of an elaborated-type-specifier ([dcl.type.elab]), a class definition (Clause [class]), a constructor declaration ([class.ctor]), or a destructor declaration ([class.dtor]), the program is ill-formed.  — end note ] [ Example:

struct S {

typedef struct S T;

S a = T();                      // OK
struct T * p;                   // error

 — 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;      // S is the class name for linkage purposes

 — end example ]

7.1.4 The friend specifier [dcl.friend]

The friend specifier is used to specify access to class members; see [class.friend].

7.1.5 The constexpr specifier [dcl.constexpr]

The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type ([basic.types]). If any declaration of a function or function template has 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 int square(int x);    // OK: declaration
constexpr int bufsz = 1024;     // OK: definition
constexpr struct pixel {        // error: pixel is a type
  int x;
  int y;
  constexpr pixel(int);         // OK: declaration
constexpr pixel::pixel(int a)
  : x(square(a)), y(square(a))  // OK: definition
  { }
constexpr pixel small(2);       // error: square not defined, so small(2)
                                // not constant ([expr.const]) so constexpr not satisfied

constexpr int square(int x) {   // OK: definition
  return x * x;
constexpr pixel large(4);       // OK: square defined
int next(constexpr int x) {     // error: not for parameters
     return x + 1;
extern constexpr int memsz;     // error: not a definition 

 — 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. constexpr functions and constexpr constructors are implicitly inline ([dcl.fct.spec]).

The definition of a constexpr function shall satisfy the following constraints:


constexpr int square(int x) 
  { return x * x; }             // OK
constexpr long long_max() 
  { return 2147483647; }        // OK
constexpr int abs(int x) 
  { return x < 0 ? -x : x; }    // OK 
constexpr void f(int x)         // error: return type is void /* ... */ 
constexpr int prev(int x)
  { return --x; }               // error: use of decrement
constexpr int g(int x, int n) { // error: body not just “return expr”
  int r = 1;
  while (--n > 0) r *= x;
  return r;

 — end example ]

In a definition of a constexpr constructor, each of the parameter types shall be a literal type. In addition, either its function-body shall be = delete or = default or it shall satisfy the following constraints:


struct Length { 
  explicit constexpr Length(int i = 0) : val(i) { }
    int val; 

 — end example ]

Function invocation substitution for a call of a constexpr function or of a constexpr constructor means implicitly converting each argument to the corresponding parameter type as if by copy-initialization,91 substituting that converted expression for each use of the corresponding parameter in the function-body, and, for constexpr functions, implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization. Such substitution does not change the meaning. [ Example:

constexpr int f(void *) { return 0; }
constexpr int f(...) { return 1; }
constexpr int g1() { return f(0); }         // calls f(void *)
constexpr int g2(int n) { return f(n); }    // calls f(...) even for n == 0
constexpr int g3(int n) { return f(n*0); }  // calls f(...)

namespace N {
  constexpr int c = 5;
  constexpr int h() { return c; }
constexpr int c = 0;
constexpr int g4() { return N::h(); }       // value is 5, c is not looked up again after the substitution

 — end example ]

For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression ([expr.const]), the program is ill-formed; no diagnostic required. For a constexpr constructor, if no argument values exist such that after function invocation substitution, every constructor call and full-expression in the mem-initializers would be a constant expression (including conversions), the program is ill-formed; no diagnostic required. [ Example:

constexpr int f(bool b)
  { return b ? throw 0 : 0; }               // OK
constexpr int f() { throw 0; }              // ill-formed, no diagnostic required

struct B {
  constexpr B(int x) : i(0) { }             // x is unused
  int i;

int global;

struct D : B {
  constexpr D() : B(global) { }             // ill-formed, no diagnostic required
                                            // lvalue-to-rvalue conversion on non-constant 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 not a constexpr function or constexpr constructor. [ Note: If the function is a member function it will still be const as described below.  — end note ] If no specialization of the template would yield a constexpr function or constexpr constructor, the program 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.

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const ([class.mfct.non-static]). [ Note: The constexpr specifier has no other effect on the function type. — end note ] The keyword const is ignored if it appears in the cv-qualifier-seq of the function declarator of the declaration of such a member function. The class of which that function is a member shall be a literal type ([basic.types]). [ Example:

class debug_flag { 
  explicit debug_flag(bool); 
  constexpr bool is_on();       // error: debug_flag not 
                                // literal type 
  bool flag; 
constexpr int bar(int x, int y) // OK 
    { return x + y + x*y; } 
// ... 
int bar(int x, int y)           // error: redefinition of bar
    { 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. If it is initialized by a constructor call, that call shall be a constant expression ([expr.const]). Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression. Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization shall be one of those allowed in a constant expression ([expr.const]). [ Example:

struct pixel { 
  int x, y; 
constexpr pixel ur = { 1294, 1024 };// OK 
constexpr pixel origin;             // error: initializer missing 

 — end example ]

The resulting converted value will include an lvalue-to-rvalue conversion ([conv.lval]) if the corresponding copy-initialization requires one.

7.1.6 Type specifiers [dcl.type]

As a general rule, at most one type-specifier is allowed in the complete decl-specifier-seq of a declaration or in a type-specifier-seq or trailing-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.

At least one type-specifier that is not a cv-qualifier is required in a declaration unless it declares a constructor, destructor or conversion function.92 A type-specifier-seq shall not define a class or enumeration unless it appears in the type-id of an alias-declaration ([dcl.typedef]) that is not the declaration of a template-declaration.

Note: enum-specifiers, class-specifiers, and typename-specifiers are discussed in [dcl.enum], [class], and [temp.res], respectively. The remaining type-specifiers are discussed in the rest of this section.  — end note ]

There is no special provision for a decl-specifier-seq that lacks a type-specifier or that has a type-specifier that only specifies cv-qualifiers. The “implicit int” rule of C is no longer supported. The cv-qualifiers [dcl.type.cv]

There are two cv-qualifiers, const and volatile. If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list of the declaration shall not be empty. [ Note: [basic.type.qualifier] and [dcl.fct] describe how cv-qualifiers affect object and function types.  — end note ] Redundant cv-qualifications are ignored. [ Note: For example, these could be introduced by typedefs. — end note ]

Note: Declaring a variable const can affect its linkage ([dcl.stc]) and its usability in constant expressions ([expr.const]). 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 ([expr.const.cast]).  — end note ]

Except that any class member declared mutable ([dcl.stc]) can be modified, any attempt to modify a const object during its lifetime ([basic.life]) results in undefined behavior. [ Example:

const int ci = 3;               // cv-qualified (initialized as required)
ci = 4;                         // ill-formed: attempt to modify const

int i = 2;                      // not cv-qualified
const int* cip;                 // pointer to const int
cip = &i;                       // OK: cv-qualified access path to unqualified
*cip = 4;                       // ill-formed: attempt to modify through ptr to const

int* ip;
ip = const_cast<int*>(cip);     // cast needed to convert const int* to int*
*ip = 4;                        // defined: *ip points to i, a non-const object

const int* ciq = new const int (3);     // initialized as required
int* iq = const_cast<int*>(ciq);        // cast required
*iq = 4;                                // undefined: modifies a const object

For another example

struct X {
  mutable int i;
  int j;
struct Y {
  X x;

const Y y;
y.x.i++;                        // well-formed: mutable member can be modified
y.x.j++;                        // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y);      // cast away const-ness of y
p->x.i = 99;                    // well-formed: mutable member can be modified
p->x.j = 99;                    // undefined: modifies a const member

 — end example ]

If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program 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. 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 ] Simple type specifiers [dcl.type.simple]

The simple type specifiers are

    nested-name-specifieropt type-name
    nested-name-specifier template simple-template-id
  decltype ( expression )

The auto specifier is a placeholder for a type to be deduced ([dcl.spec.auto]). The other simple-type-specifiers specify either a previously-declared user-defined type or one of the fundamental types ([basic.fundamental]). Table [tab:simple.type.specifiers] summarizes the valid combinations of simple-type-specifiers and the types they specify.

Table 10simple-type-specifiers and the types they specify
Specifier(s) Type
type-name the type named
simple-template-id the type as defined in [temp.names]
char “char”
unsigned char “unsigned char”
signed char “signed char”
char16_t “char16_t”
char32_t “char32_t”
bool “bool”
unsigned “unsigned int”
unsigned int “unsigned int”
signed “int”
signed int “int”
int “int”
unsigned short int “unsigned short int”
unsigned short “unsigned short int”
unsigned long int “unsigned long int”
unsigned long “unsigned long int”
unsigned long long int “unsigned long long int”
unsigned long long “unsigned long long int”
signed long int “long int”
signed long “long int”
signed long long int “long long int”
signed long long “long long int”
long long int “long long int”
long long “long long int”
long int “long int”
long “long int”
signed short int “short int”
signed short “short int”
short int “short int”
short “short int”
wchar_t “wchar_t”
float “float”
double “double”
long double “long double”
void “void”
auto placeholder for a type to be deduced
decltype(expression) the type as defined below

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 and certain bit-fields ([class.bit]) are represented as signed or unsigned quantities. The signed specifier forces char objects and bit-fields to be signed; it is redundant in other contexts.  — end note ]

The type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), 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 (Clause [expr]).


const int&& foo(); 
int i; 
struct A { double x; };
const A* a = new A(); 
decltype(foo()) x1 = i;         // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

 — end example ]

Note: in the case where the operand of a decltype-specifier is a function call and the return type of the function is a class type, a special rule ([expr.call]) ensures that the return type is not required to be complete (as it would be if the call appeared in a sub-expression or outside of a decltype-specifier). 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. [ Example:

template<class T> struct A { ~A() = delete; };
template<class T> auto h()
  -> A<T>;
template<class T> auto i(T)     // identity
  -> T;
template<class T> auto f(T)     // #1
  -> decltype(i(h<T>()));       // forces completion of A<T> and implicitly uses
                                // A<T>::~A() for the temporary introduced by the
                                // use of h(). (A temporary is not introduced
                                // as a result of the use of i().)
template<class T> auto f(T)     // #2
  -> void;
auto g() -> void {
  f(42);                        // OK: calls #2. (#1 is not a viable candidate: type
                                // deduction fails ([temp.deduct]) because A<int>::~A()
                                // is implicitly used in its decltype-specifier)
template<class T> auto q(T)
  -> decltype((h<T>()));        // does not force completion of A<T>; A<T>::~A() is
                                // not implicitly used within the context of this decltype-specifier
void r() {
  q(42);                        // Error: deduction against q succeeds, so overload resolution
                                // selects the specialization “q(T) -> decltype((h<T>())) [with T=int]”.
                                // The return type is A<int>, so a temporary is introduced and its
                                // destructor is used, so the program is ill-formed.

 — end example ] — end note ] Elaborated type specifiers [dcl.type.elab]

    class-key attribute-specifier-seqopt nested-name-specifieropt identifier
    class-key nested-name-specifieropt templateopt simple-template-id
    enum nested-name-specifieropt identifier

An attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of a declaration. If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization ([temp.expl.spec]), an explicit instantiation ([temp.explicit]) or it has one of the following forms:

class-key attribute-specifier-seqopt identifier ;
friend class-key ::opt identifier ;
friend class-key ::opt simple-template-id ;
friend class-key nested-name-specifier identifier ;
friend class-key nested-name-specifier templateopt simple-template-id ;

In the first case, the attribute-specifier-seq, if any, appertains to the class being declared; the attributes in the attribute-specifier-seq are thereafter considered attributes of the class whenever it is named.

[basic.lookup.elab] describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed. [ Note: This implies that, within a class template with a template type-parameter T, the declaration

friend class T;

is ill-formed. However, the similar declaration friend T; is allowed ([class.friend]).  — end note ]

The class-key or enum keyword present in the elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. This rule also applies to the form of elaborated-type-specifier that declares a class-name or friend class since it can be construed as referring to the definition of the class. Thus, in any elaborated-type-specifier, the enum keyword shall be used to refer to an enumeration ([dcl.enum]), the union class-key shall be used to refer to a union (Clause [class]), and either the class or struct class-key shall be used to refer to a class (Clause [class]) declared using the class or struct class-key. [ Example:

enum class E { a, b };
enum E x = E::a;                // OK

 — end example ] auto specifier [dcl.spec.auto]

The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.

The auto type-specifier may appear with a function declarator with a trailing-return-type ([dcl.fct]) in any context where such a declarator is valid.

Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared shall not appear in the initializer expression. This use of auto is allowed when declaring variables in a block ([stmt.block]), in namespace scope ([basic.scope.namespace]), and in a for-init-statement ([stmt.for]). auto shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer.


auto x = 5;                 // OK: x has type int
const auto *v = &x, u = 6;  // OK: v has type const int*, u has type const int
static auto y = 0.0;        // OK: y has type double
auto int r;                 // error: auto is not a storage-class-specifier

 — end example ]

The auto type-specifier can also be used in declaring a variable in the condition of a selection statement ([stmt.select]) or an iteration statement ([stmt.iter]), in the type-specifier-seq in the new-type-id or type-id of a new-expression ([expr.new]), in a for-range-declaration, and in declaring a static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition ([class.static.data]).

A program that uses auto in a context not explicitly allowed in this section is ill-formed.

Once the type of a declarator-id has been determined according to [dcl.meaning], the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list ([dcl.init.list]), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call ([temp.deduct.call]), where P is a function template parameter type and the initializer for d is the corresponding argument. If the deduction fails, the declaration is ill-formed. [ Example:

auto x1 = { 1, 2 };         // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 };       // error: cannot deduce element type

 — end example ]

If the list of declarators contains more than one declarator, the type of each declared variable is determined as described above. If the type deduced for the template parameter U is not the same in each deduction, the program is ill-formed.


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 ]

7.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.


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 ]

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.

If the enum-key is followed by a nested-name-specifier, the enum-specifier shall refer to an enumeration that was previously declared directly in the class or namespace to which the nested-name-specifier refers (i.e., neither inherited nor introduced by a using-declaration), and the enum-specifier shall appear in a namespace enclosing the previous 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 enum-base; if not explicitly specified, the underlying type of a scoped enumeration type is int. In 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 ([expr.const]); if the initializing value of an enumerator cannot be represented by the underlying type, the program is ill-formed. If the underlying type is not fixed, the type of each enumerator is the type of its initializing value:

  • If an initializer is specified for an enumerator, the initializing value has the same type as the expression and the constant-expression shall be an integral constant expression ([expr.const]).

  • If no initializer is specified for the first enumerator, the initializing value has an unspecified integral type.

  • Otherwise the type of the initializing value is the same as the type of the initializing value 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.

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 one's 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.93

Two enumeration types are layout-compatible if they have the same underlying type.

The value of an enumerator or an object of an unscoped enumeration type is converted to an integer by integral promotion ([conv.prom]). [ 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;                    // error: type mismatch,
                                // no conversion from int to color

int i = yellow;                 // OK: yellow converted to integral value 1
                                // integral promotion

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;               // error: no Col to int conversion
Col y = Col::red;
if (y) { }                      // error: no Col to bool conversion

 — 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;                  // OK 
  d = left;                     // OK 
  d = direction::right;         // OK 

enum class altitude { high='h', low='l' }; 

void h()  { 
  altitude a;                   // OK 
  a = high;                     // error: high not in scope 
  a = altitude::low;            // OK 

 — 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;                  // error: direction not in scope
  int i;
  i = p->f(left);               // error: left not in scope
  i = p->f(X::right);           // OK
  i = p->f(p->left);            // OK
  // ...

 — end example ]

This set of values is used to define promotion and conversion semantics for the enumeration type. It does not preclude an expression of enumeration type from having a value that falls outside this range.

7.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.

The outermost declarative region of a translation unit is a namespace; see [basic.scope.namespace].

7.3.1 Namespace definition [namespace.def]

The identifier in an original-namespace-definition shall not have been previously defined in the declarative region in which the original-namespace-definition appears. The identifier in an original-namespace-definition is the name of the namespace. Subsequently in that declarative region, it is treated as an original-namespace-name.

The original-namespace-name in an extension-namespace-definition shall have previously been defined in an original-namespace-definition in the same declarative region.

Every namespace-definition shall appear in the global scope or in a namespace scope ([basic.scope.namespace]).

Because a namespace-definition contains declarations in its namespace-body and a namespace-definition is itself a declaration, it follows that namespace-definitions can be nested. [ Example:

namespace Outer {
  int i;
  namespace Inner {
    void f() { i++; }           // Outer::i
    int i;
    void g() { i++; }           // Inner::i

 — end example ]

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();   // enclosing namespaces are the global namespace, Q, and Q::V
    class C { void m(); };
  void V::f() { // enclosing namespaces are the global namespace, Q, and Q::V
    extern void h();  // ... so this declares Q::V::h
  void V::C::m() { // enclosing namespaces are the global namespace, Q, and Q::V

 — end example ]

If the optional initial inline keyword appears in a namespace-definition for a particular namespace, that namespace is declared to be an inline namespace. The inline keyword may be used on an extension-namespace-definition only if it was previously used on the original-namespace-definition for that namespace.

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 ([basic.lookup.argdep]) whenever one of them is, and a using-directive ([namespace.udir]) that names the inline namespace is implicitly inserted into the enclosing namespace as for an unnamed namespace ([namespace.unnamed]). Furthermore, each member of the inline namespace can subsequently be explicitly instantiated ([temp.explicit]) or explicitly specialized ([temp.expl.spec]) 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. 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, all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.94Example:

namespace { int i; }            // unique  ::i
void f() { i++; }               // unique  ::i++

namespace A {
  namespace {
    int i;                      // A:: unique  ::i
    int j;                      // A:: unique  ::j
  void g() { i++; }             // A:: unique  ::i++

using namespace A;
void h() {
  i++;                          // error: unique  ::i or A:: unique  ::i
  A::i++;                       // A:: unique  ::i
  j++;                          // A:: unique  ::j

 — end example ]

Although entities in an unnamed namespace might have external linkage, they are effectively qualified by a name unique to their translation unit and therefore can never be seen from any other translation unit. Namespace member definitions [namespace.memdef]

Members (including explicit specializations of templates ([temp.expl.spec])) of a namespace can be defined within that namespace. [ Example:

namespace X {
  void f() { /* ... */ }

 — end example ]

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() { /* ... */ }     // OK
  void V::g() { /* ... */ }     // error: g() is not yet a member of V
  namespace V {
    void g();

namespace R {
  void Q::V::g() { /* ... */ }  // error: R doesn't enclose Q

 — end example ]

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup ([basic.lookup.unqual]) or by qualified lookup ([basic.lookup.qual]) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function 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:

// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
  class X {
    friend void f(X);           // A::f(X) is a friend
    class Y {
      friend void g();          // A::g is a friend
      friend void h(int);       // A::h is a friend
                                // ::h not considered
      friend void f2<>(int);    // ::f2<>(int) is a friend

  // A::f, A::g and A::h are not visible here
  X x;
  void g() { f(x); }            // definition of A::g
  void f(X) { /* ... */}       // definition of A::f
  void h(int) { /* ... */ }    // definition of A::h
  // A::f, A::g and A::h are visible here and known to be friends

using A::x;

void h() {
  A::X::f(x);                   // error: f is not a member of A::X
  A::X::Y::g();                 // error: g is not a member of A::X::Y

 — end example ]

this implies that the name of the class or function is unqualified.

7.3.2 Namespace alias [namespace.alias]

The identifier in a namespace-alias-definition is a synonym for the name of the namespace denoted by the qualified-namespace-specifier and becomes a namespace-alias. [ Note: When looking up a namespace-name in a namespace-alias-definition, only namespace names are considered, see [basic.lookup.udir].  — end note ]

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;          // OK: duplicate
namespace CWVLN = CWVLN;

 — end example ]

A namespace-name or namespace-alias shall not be declared as the name of any other entity in the same declarative region. A namespace-name defined at global scope shall not be declared as the name of any other entity in any global scope of the program. No diagnostic is required for a violation of this rule by declarations in different translation units.

7.3.3 The using declaration [namespace.udecl]

A using-declaration introduces a name into the declarative region in which the using-declaration appears.

    using typenameopt nested-name-specifier unqualified-id ;
    using :: unqualified-id ;

The member name specified in a using-declaration is declared in the declarative region in which the using-declaration appears. [ Note: Only the specified name is so declared; specifying an enumeration name in a using-declaration does not declare its enumerators in the using-declaration's declarative region.  — end note ] If a using-declaration names a constructor ([class.qual]), it implicitly declares a set of constructors in the class in which the using-declaration appears ([class.inhctor]); otherwise the name specified in a using-declaration is a synonym for the name of some entity declared elsewhere.

Every using-declaration is a declaration and a member-declaration and so can 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'); }       // calls B::f(char)
  void g(int) { g('c'); }       // recursively calls D::g(int)

 — end example ]

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined. If such a using-declaration names a constructor, the nested-name-specifier shall name a direct base class of the class being defined; otherwise it introduces the set of declarations found by member name lookup ([class.member.lookup], [class.qual]). [ Example:

class C {
  int g();

class D2 : public B {
  using B::f;                   // OK: B is a base of D2
  using B::e;                   // OK: e is an enumerator of base B
  using B::x;                   // OK: x is a union member of base B
  using C::g;                   // error: C isn't a base of D2

 — 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 an assignment operator brought from a base class into a derived class scope has the signature of a copy/move assignment operator for the derived class ([class.copy]), the using-declaration does not by itself suppress the implicit declaration of the derived class assignment operator; the copy/move assignment operator from the base class is hidden or overridden by the implicitly-declared copy/move 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>;           // ill-formed
  using A::X<int>;              // ill-formed

 — end example ]

A using-declaration shall not name a namespace.

A using-declaration shall not name a scoped enumerator.

A using-declaration for a class member shall be a member-declaration. [ Example:

struct X {
  int i;
  static int s;

void f() {
  using X::i;       // error: X::i is a class member
                    // and this is not a member declaration.
  using X::s;       // error: X::s is a class member
                    // and this is not a member declaration.

 — end example ]

Members declared by a using-declaration can be referred to by explicit qualification just like other member names ([namespace.qual]). In a using-declaration, a prefix :: refers to the global namespace. [ Example:

void f();

namespace A {
  void g();

namespace X {
  using ::f;        // global f
  using A::g;       // A's g

void h(){
  X::f();           // calls ::f
  X::g();           // calls A::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;
  using A::i;       // OK: double declaration

void f() {
  using A::i;
  using A::i;       // error: double declaration

struct B {
  int i;

struct X : B {
  using B::i;
  using B::i;       // error: double member declaration

 — end example ]

The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration. Definitions added to the namespace after the using-declaration are not considered when a use of the name is made. [ Example:

namespace A {
  void f(int);

using A::f;         // f is a synonym for A::f;
                    // that is, for A::f(int).
namespace A {
  void f(char);

void foo() {
  f('a');           // calls f(int),
}                   // even though f(char) exists.

void bar() {
  using A::f;       // f is a synonym for A::f;
                    // that is, for A::f(int) and A::f(char).
  f('a');           // calls f(char)

 — 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 ([basic.scope]) 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);     // OK: hides struct g

void func() {
  int i;
  using B::i;       // error: i declared twice
  void f(char);
  using B::f;       // OK: each f is a function
  f(3.5);           // calls B::f(double)
  using B::g;
  g('a');           // calls B::g(char)
  struct g g1;      // g1 has class type B::g
  using B::x;
  using A::x;       // OK: hides struct B::x
  x = 99;           // assigns to A::x
  struct x x1;      // x1 has class type B::x

 — end example ]

If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed. [ Note: Two using-declarations may introduce functions with the same name and the same parameter types. 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;       // B::f(int) and B::f(double)
  using C::f;       // C::f(int), C::f(double), and C::f(char)
  f('h');           // calls C::f(char)
  f(1);             // error: ambiguous: B::f(int) or C::f(int)?
  void f(int);      // error: f(int) conflicts with C::f(int) and B::f(int)

 — end example ]  — end note ]

When a using-declaration brings names from a base class into a derived class scope, 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 ([dcl.fct]), cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). [ Note: For using-declarations that name a constructor, see [class.inhctor].  — end note ] [ 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);      // OK: D::f(int) overrides B::f(int);

  using B::g;
  void g(char);     // OK

  using B::h;
  void h(int);      // OK: D::h(int) hides B::h(int)

void k(D* p){
  p->f(1);          // calls D::f(int)
  p->f('a');        // calls B::f(char)
  p->g(1);          // calls B::g(int)
  p->g('a');        // calls D::g(char)

 — end example ]

For the purpose of overload resolution, the functions which are introduced by a using-declaration into a derived class will be 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.

The access rules for inheriting constructors are specified in [class.inhctor]; otherwise all instances of the name mentioned in a using-declaration shall be accessible. In particular, if a derived class uses a using-declaration 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-declaration shall be visible in the scope of at least one of the direct base classes of the class where the using-declaration is specified. [ Note: Because a using-declaration designates a base class member (and not a member subobject or a member function of a base class subobject), a using-declaration cannot be used to resolve inherited member ambiguities. For 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();    // ambiguous: B::x or C::x

 — end note ]

The alias created by the using-declaration has the usual accessibility for a member-declaration. [ Note: A using-declaration that names a constructor does not create aliases; see [class.inhctor] for the pertinent accessibility rules.  — end note ] [ Example:

class A {
    void f(char);
    void f(int);
    void g();

class B : public A {
  using A::f;       // error: A::f(char) is inaccessible
  using A::g;       // B::g is a public synonym for A::g

 — end example ]

If a using-declaration uses the keyword typename and specifies a dependent name ([temp.dep]), the name introduced by the using-declaration is treated as a typedef-name ([dcl.typedef]).

7.3.4 Using directive [namespace.udir]

    attribute-specifier-seqopt using  namespace nested-name-specifieropt namespace-name ;

A using-directive shall not appear in class scope, but may appear in namespace scope or in block scope. [ Note: When looking up a namespace-name in a using-directive, only namespace names are considered, see [basic.lookup.udir].  — end note ] The optional attribute-specifier-seq appertains to the using-directive.

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 ([basic.lookup.unqual]), 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;        // OK, C::i visible in B and hides A::i
  namespace D {
    using namespace B;
    using namespace C;
    void f2() {
      i = 5;        // ambiguous, B::C::i or A::i?
  void f3() {
    i = 5;          // uses A::i
void f4() {
  i = 5;            // ill-formed; neither i is visible

 — end example ]

For unqualified lookup ([basic.lookup.unqual]), 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;            // error: both M::i and N::i are visible

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;    // B::i hides A::i
    using namespace D;
    int k = 89;     // no problem yet
    int l = k;      // ambiguous: C::k or D::k
    int m = i;      // B::i hides A::i
    int n = j;      // D::j hides B::j

 — end example ]

If a namespace is extended by an extension-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extension-namespace-definition can be used after the extension-namespace-definition.

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);             // error: name X found in two namespaces
  g();              // okay: name g refers to the same entity
  h();              // okay: overload resolution selects A::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.96Example:

namespace D {
  int d1;
  void f(char);
using namespace D;

int d1;             // OK: no conflict with D::d1

namespace E {
  int e;
  void f(int);

namespace D {       // namespace extension
  int d2;
  using namespace E;
  void f(int);

void f() {
  d1++;             // error: ambiguous ::d1 or D::d1?
  ::d1++;           // OK
  D::d1++;          // OK
  d2++;             // OK: D::d2
  e++;              // OK: E::e
  f(1);             // error: ambiguous: D::f(int) or E::f(int)?
  f('a');           // OK: D::f(char)

 — end example ]

During name lookup in a class hierarchy, some ambiguities may be resolved by considering whether one member hides the other along some paths ([class.member.lookup]). There is no such disambiguation when considering the set of names found as a result of following using-directives.

7.4 The asm declaration [dcl.asm]

An asm declaration has the form

    asm ( string-literal ) ;

The asm declaration is conditionally-supported; its meaning is implementation-defined. [ Note: Typically it is used to pass information through the implementation to an assembler.  — end note ]

7.6 Attributes [dcl.attr]

7.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 ]

In an attribute-list, an ellipsis may appear only if that attribute's specification permits it. An attribute followed by an ellipsis is a pack expansion ([temp.variadic]). An attribute-specifier that contains no attributes has no effect. The order in which the attribute-tokens appear in an attribute-list is not significant. If a keyword ([lex.key]) or an alternative token ([lex.digraph]) that satisfies the syntactic requirements of an identifier ([lex.name]) is contained in an attribute-token, it is considered an identifier. No name lookup ([basic.lookup]) is performed on any of the identifiers contained in an attribute-token. The attribute-token determines additional requirements on the attribute-argument-clause (if any). The use of an attribute-scoped-token is conditionally-supported, with implementation-defined behavior. [ Note: Each implementation should choose a distinctive name for the attribute-namespace in an attribute-scoped-token.  — end note ]

Each attribute-specifier-seq is said to appertain to some entity or statement, identified by the syntactic context where it appears (Clause [stmt.stmt], Clause [dcl.dcl], Clause [dcl.decl]). If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed. If an attribute-specifier-seq appertains to a friend declaration ([class.friend]), that declaration shall be a definition. No attribute-specifier-seq shall appertain to an explicit instantiation ([temp.explicit]).

For an attribute-token not specified in this International Standard, the behavior is implementation-defined.

Two consecutive left square bracket tokens shall appear only when introducing an attribute-specifier. [ 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; }()]);  // error: malformed attribute on a nested
                                // declarator-id and not a function-style cast of
                                // an element of p.
  y[[] { return 2; }()] = 2;    // error even though attributes are not allowed
                                // in this context.

 — end example ]

7.6.2 Alignment specifier [dcl.align]

An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, the formal parameter of a catch clause ([except.handle]), or a variable declared with the register storage class specifier. An alignment-specifier may also be applied to the declaration of a class or enumeration type. An alignment-specifier with an ellipsis is a pack expansion ([temp.variadic]).

When the alignment-specifier is of the form alignas( assignment-expression ):

  • the assignment-expression shall be an integral constant expression

  • if the constant expression evaluates to a fundamental alignment, the alignment requirement of the declared entity shall be the specified fundamental alignment

  • if the constant expression evaluates to an extended alignment and the implementation supports that alignment in the context of the declaration, the alignment of the declared entity shall be that alignment

  • if the constant expression evaluates to an extended alignment and the implementation does not support that alignment in the context of the declaration, the program is ill-formed

  • if the constant expression evaluates to zero, the alignment specifier shall have no effect

  • otherwise, the program is ill-formed.

When the alignment-specifier is of the form alignas( type-id ), it shall have the same effect as alignas(alignof(type-id)) ([expr.alignof]).

When multiple alignment-specifiers are specified for an entity, the alignment requirement shall be set to the strictest specified alignment.

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 were omitted (including those in other declarations).

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.


// Translation unit #1:
struct S { int x; } s, p = &s;

// Translation unit #2:
struct alignas(16) S;           // error: definition of S lacks alignment; no
extern S* p;                    // diagnostic required

 — end example ]

Example: An aligned buffer with an alignment requirement of A and holding N elements of type T other than char, signed char, or unsigned char 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 ]


alignas(double) void f();                         // error: alignment applied to function
alignas(double) unsigned char c[sizeof(double)];  // array of characters, suitably aligned for a double
extern unsigned char c[sizeof(double)];           // no alignas necessary
  extern unsigned char c[sizeof(double)];         // error: different alignment in declaration

 — end example ]

7.6.3 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 ]


[[ noreturn ]] void f() {
  throw "error";        // OK

[[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
  if (i > 0)
    throw "positive";

 — end example ]

7.6.4 Carries dependency attribute [dcl.attr.depend]

The attribute-token carries_dependency specifies dependency propagation into and out of functions. 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 of a parameter-declaration in a function declaration or lambda, in which case it specifies that the initialization of the parameter carries a dependency to ([intro.multithread]) each lvalue-to-rvalue conversion ([conv.lval]) of that object. The attribute may also be applied to the declarator-id of a function declaration, in which case it specifies that the return value, if any, carries a dependency to the evaluation of the function call expression.

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 ]


/* 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);

[[carries_dependency]] int g(int* x, int* y) {
  return kill_dependency(foo_array[*x][*y]);

/* Translation unit B. */

[[carries_dependency]] struct foo* f(int i);
[[carries_dependency]] int* g(int* x, int* y);

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 argument has a carries_dependency attribute, but its first argument 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 ]