8 Declarators [dcl.decl]

8.3 Meaning of declarators [dcl.meaning]

A declarator contains exactly one declarator-id; it names the identifier that is declared. An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions ([class.ctor], [class.conv], [class.dtor], [over.oper]) and for the declaration of template specializations or partial specializations ([temp.spec]). When the declarator-id is qualified, the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers (or, in the case of a namespace, of an element of the inline namespace set of that namespace ([namespace.def])) or to a specialization thereof; the member shall not merely have been introduced by a using-declaration in the scope of the class or namespace nominated by the nested-name-specifier of the declarator-id. The nested-name-specifier of a qualified declarator-id shall not begin with a decltype-specifier. [ Note: If the qualifier is the global :: scope resolution operator, the declarator-id refers to a name declared in the global namespace scope.  — end note ] The optional attribute-specifier-seq following a declarator-id appertains to the entity that is declared.

A static, thread_local, extern, mutable, friend, inline, virtual, constexpr, explicit, or typedef specifier applies directly to each declarator-id in an init-declarator-list or member-declarator-list; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.

Thus, a declaration of a particular identifier has the form

T D

where T is of the form attribute-specifier-seqopt decl-specifier-seq and D is a declarator. Following is a recursive procedure for determining the type specified for the contained declarator-id by such a declaration.

First, the decl-specifier-seq determines a type. In a declaration

T D

the decl-specifier-seq T determines the type T. [ Example: in the declaration

int unsigned i;

the type specifiers int unsigned determine the type “unsigned int” ([dcl.type.simple]).  — end example ]

In a declaration attribute-specifier-seqopt T D where D is an unadorned identifier the type of this identifier is “T”.

In a declaration T D where D has the form

( D1 )

the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration

T D1

Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.

8.3.1 Pointers [dcl.ptr]

In a declaration T D where D has the form

* attribute-specifier-seqopt cv-qualifier-seqopt D1

and the type of the identifier in the declaration T D1 is “ derived-declarator-type-list T”, then the type of the identifier of D is “ derived-declarator-type-list cv-qualifier-seq pointer to T”. The cv-qualifiers apply to the pointer and not to the object pointed to. Similarly, the optional attribute-specifier-seq ([dcl.attr.grammar]) appertains to the pointer and not to the object pointed to.

Example: the declarations

const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
int i, *p, *const cp = &i;

declare ci, a constant integer; pc, a pointer to a constant integer; cpc, a constant pointer to a constant integer; ppc, a pointer to a pointer to a constant integer; i, an integer; p, a pointer to integer; and cp, a constant pointer to integer. The value of ci, cpc, and cp cannot be changed after initialization. The value of pc can be changed, and so can the object pointed to by cp. Examples of some correct operations are

i = ci;
*cp = ci;
pc++;
pc = cpc;
pc = p;
ppc = &pc;

Examples of ill-formed operations are

ci = 1;             // error
ci++;               // error
*pc = 2;            // error
cp = &ci;           // error
cpc++;              // error
p = pc;             // error
ppc = &p;           // error

Each is unacceptable because it would either change the value of an object declared const or allow it to be changed through a cv-unqualified pointer later, for example:

*ppc = &ci;         // OK, but would make p point to ci ...
                    // ... because of previous error
*p = 5;             // clobber ci

 — end example ]

See also [expr.ass] and [dcl.init].

Note: Forming a pointer to reference type is ill-formed; see [dcl.ref]. Forming a function pointer type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see [dcl.fct]. Since the address of a bit-field ([class.bit]) cannot be taken, a pointer can never point to a bit-field.  — end note ]

8.3.2 References [dcl.ref]

In a declaration T D where D has either of the forms

& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1

and the type of the identifier in the declaration T D1 is “ derived-declarator-type-list T”, then the type of the identifier of D is “ derived-declarator-type-list reference to T”. The optional attribute-specifier-seq appertains to the reference type. Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), in which case the cv-qualifiers are ignored. [ Example:

typedef int& A;
const A aref = 3;   // ill-formed; lvalue reference to non-const initialized with rvalue

The type of aref is “lvalue reference to int”, not “lvalue reference to const int”.  — end example ] [ Note: A reference can be thought of as a name of an object.  — end note ] A declarator that specifies the type “reference to cv void” is ill-formed.

A reference type that is declared using & is called an lvalue reference, and a reference type that is declared using && is called an rvalue reference. Lvalue references and rvalue references are distinct types. Except where explicitly noted, they are semantically equivalent and commonly referred to as references.

Example:

void f(double& a) { a += 3.14; }
// ...
double d = 0;
f(d);

declares a to be a reference parameter of f so the call f(d) will add 3.14 to d.

int v[20];
// ...
int& g(int i) { return v[i]; }
// ...
g(3) = 7;

declares the function g() to return a reference to an integer so g(3)=7 will assign 7 to the fourth element of the array v. For another example,

struct link {
  link* next;
};

link* first;

void h(link*& p) {  // p is a reference to pointer
  p->next = first;
  first = p;
  p = 0;
}

void k() {
   link* q = new link;
   h(q);
}

declares p to be a reference to a pointer to link so h(q) will leave q with the value zero. See also [dcl.init.ref].  — end example ]

It is unspecified whether or not a reference requires storage ([basic.stc]).

There shall be no references to references, no arrays of references, and no pointers to references. The declaration of a reference shall contain an initializer ([dcl.init.ref]) except when the declaration contains an explicit extern specifier ([dcl.stc]), is a class member ([class.mem]) declaration within a class definition, or is the declaration of a parameter or a return type ([dcl.fct]); see [basic.def]. A reference shall be initialized to refer to a valid object or function. [ Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a null pointer, which causes undefined behavior. As described in [class.bit], a reference cannot be bound directly to a bit-field.  — end note ]

If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.simple]) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR. [ Note: This rule is known as reference collapsing.  — end note ] [ Example:

int i;
typedef int& LRI;
typedef int&& RRI;

LRI& r1 = i;                    // r1 has the type int&
const LRI& r2 = i;              // r2 has the type int&
const LRI&& r3 = i;             // r3 has the type int&

RRI& r4 = i;                    // r4 has the type int&
RRI&& r5 = 5;                   // r5 has the type int&&

decltype(r2)& r6 = i;           // r6 has the type int&
decltype(r2)&& r7 = i;          // r7 has the type int&

 — end example ]

Note: Forming a reference to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see [dcl.fct].  — end note ]

8.3.3 Pointers to members [dcl.mptr]

In a declaration T D where D has the form

nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1

and the nested-name-specifier denotes a class, and the type of the identifier in the declaration T D1 is “ derived-declarator-type-list T”, then the type of the identifier of D is “ derived-declarator-type-list cv-qualifier-seq pointer to member of class nested-name-specifier of type T”. The optional attribute-specifier-seq ([dcl.attr.grammar]) appertains to the pointer-to-member.

Example:

struct X {
  void f(int);
  int a;
};
struct Y;

int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;

declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member of X of type double and a pointer to a member of Y of type char respectively. The declaration of pmd is well-formed even though X has no members of type double. Similarly, the declaration of pmc is well-formed even though Y is an incomplete type. pmi and pmf can be used like this:

X obj;
// ...
obj.*pmi = 7;       // assign 7 to an integer
                    // member of obj
(obj.*pmf)(7);      // call a function member of obj
                    // with the argument 7

 — end example ]

A pointer to member shall not point to a static member of a class ([class.static]), a member with reference type, or “cv void”.

Note: See also [expr.unary] and [expr.mptr.oper]. The type “pointer to member” is distinct from the type “pointer”, that is, a pointer to member is declared only by the pointer to member declarator syntax, and never by the pointer declarator syntax. There is no “reference-to-member” type in C++.  — end note ]

8.3.4 Arrays [dcl.array]

In a declaration T D where D has the form

D1 [ constant-expressionopt ] attribute-specifier-seqopt

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed. T is called the array element type; this type shall not be a reference type, the (possibly cv-qualified) type void, a function type or an abstract class type. If the constant-expression ([expr.const]) is present, it shall be a converted constant expression of type std::size_t and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “ derived-declarator-type-list array of N T”. An object of array type contains a contiguously allocated non-empty set of N subobjects of type T. Except as noted below, if the constant expression is omitted, the type of the identifier of D is “ derived-declarator-type-list array of unknown bound of T”, an incomplete object type. The type “ derived-declarator-type-list array of N T” is a different type from the type “ derived-declarator-type-list array of unknown bound of T”, see [basic.types]. Any type of the form “ cv-qualifier-seq array of N T” is adjusted to “array of N cv-qualifier-seq T”, and similarly for “array of unknown bound of T”. The optional attribute-specifier-seq appertains to the array. [ Example:

typedef int A[5], AA[2][3];
typedef const A CA;             // type is “array of 5 const inttypedef const AA CAA;           // type is “array of 2 array of 3 const int

 — end example ] [ Note: An “array of N cv-qualifier-seq T” has cv-qualified type; see [basic.type.qualifier].  — end note ]

An array can be constructed from one of the fundamental types (except void), from a pointer, from a pointer to member, from a class, from an enumeration type, or from another array.

When several “array of” specifications are adjacent, a multidimensional array type is created; only the first of the constant expressions that specify the bounds of the arrays may be omitted. In addition to declarations in which an incomplete object type is allowed, an array bound may be omitted in some cases in the declaration of a function parameter ([dcl.fct]). An array bound may also be omitted when the declarator is followed by an initializer ([dcl.init]) or when a declarator for a static data member is followed by a brace-or-equal-initializer ([class.mem]). In both cases the bound is calculated from the number of initial elements (say, N) supplied ([dcl.init.aggr]), and the type of the identifier of D is “array of N T”. Furthermore, if there is a preceding declaration of the entity in the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.

Example:

float fa[17], *afp[17];

declares an array of float numbers and an array of pointers to float numbers. For another example,

static int x3d[3][5][7];

declares a static three-dimensional array of integers, with rank 3 × 5 × 7. In complete detail, x3d is an array of three items; each item is an array of five arrays; each of the latter arrays is an array of seven integers. Any of the expressions x3d, x3d[i], x3d[i][j], x3d[i][j][k] can reasonably appear in an expression. Finally,

extern int x[10];
struct S {
  static int y[10];
};

int x[];                      // OK: bound is 10
int S::y[];                   // OK: bound is 10

void f() {
  extern int x[];
  int i = sizeof(x);          // error: incomplete object type
}

 — end example ]

Note: conversions affecting expressions of array type are described in [conv.array]. Objects of array types cannot be modified, see [basic.lval].  — end note ]

Note: Except where it has been declared for a class ([over.sub]), the subscript operator [] is interpreted in such a way that E1[E2] is identical to *((E1)+(E2)). Because of the conversion rules that apply to +, if E1 is an array and E2 an integer, then E1[E2] refers to the E2-th member of E1. Therefore, despite its asymmetric appearance, subscripting is a commutative operation.

A consistent rule is followed for multidimensional arrays. If E is an n-dimensional array of rank i × j × … × k, then E appearing in an expression that is subject to the array-to-pointer conversion ([conv.array]) is converted to a pointer to an (n-1)-dimensional array with rank j × … × k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n-1)-dimensional array, which itself is immediately converted into a pointer.

Example: consider

int x[3][5];

Here x is a 3 × 5 array of integers. When x appears in an expression, it is converted to a pointer to (the first of three) five-membered arrays of integers. In the expression x[i] which is equivalent to *(x+i), x is first converted to a pointer as described; then x+i is converted to the type of x, which involves multiplying i by the length of the object to which the pointer points, namely five integer objects. The results are added and indirection applied to yield an array (of five integers), which in turn is converted to a pointer to the first of the integers. If there is another subscript the same argument applies again; this time the result is an integer.  — end example ]  — end note ]

Note: It follows from all this that arrays in C++ are stored row-wise (last subscript varies fastest) and that the first subscript in the declaration helps determine the amount of storage consumed by an array but plays no other part in subscript calculations.  — end note ]

8.3.5 Functions [dcl.fct]

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt

and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list noexceptopt function of (parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returning T”, where the optional noexcept is present if and only if the exception specification ([except.spec]) is non-throwing. The optional attribute-specifier-seq appertains to the function type.

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type

and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, T shall be the single type-specifier auto. The type of the declarator-id in D is “derived-declarator-type-list noexceptopt function of (parameter-declaration-clause) cv-qualifier-seqopt ref-qualifieropt returning U”, where U is the type specified by the trailing-return-type, and where the optional noexcept is present if and only if the exception specification is non-throwing. The optional attribute-specifier-seq appertains to the function type.

The parameter-declaration-clause determines the arguments that can be specified, and their processing, when the function is called. [ Note: the parameter-declaration-clause is used to convert the arguments specified on the function call; see [expr.call].  — end note ] If the parameter-declaration-clause is empty, the function takes no arguments. A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list. Except for this special case, a parameter shall not have type cv void. If the parameter-declaration-clause terminates with an ellipsis or a function parameter pack ([temp.variadic]), the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument and are not function parameter packs. Where syntactically correct and where “...” is not part of an abstract-declarator, “, ...” is synonymous with “...”. [ Example: the declaration

int printf(const char*, ...);

declares a function that can be called with varying numbers and types of arguments.

printf("hello world");
printf("a=%d b=%d", a, b);

However, the first argument must be of a type that can be converted to a const char*  — end example ] [ Note: The standard header <cstdarg> contains a mechanism for accessing arguments passed using the ellipsis (see [expr.call] and [support.runtime]).  — end note ]

A single name can be used for several different functions in a single scope; this is function overloading (Clause [over]). All declarations for a function shall agree exactly in both the return type and the parameter-type-list. The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types.  — end note ]

A function type with a cv-qualifier-seq or a ref-qualifier (including a type named by typedef-name ([dcl.typedef], [temp.param])) shall appear only as:

Example:

typedef int FIC(int) const;
FIC f;              // ill-formed: does not declare a member function
struct S {
  FIC f;            // OK
};
FIC S::*pm = &S::f; // OK

 — end example ]

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: a function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types.  — end note ] [ Example:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

 — end example ]

The return type, the parameter-type-list, the ref-qualifier, the cv-qualifier-seq, and the exception specification, but not the default arguments ([dcl.fct.default]), are part of the function type. [ Note: Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions.  — end note ]

Example: the declaration

int fseek(FILE*, long, int);

declares a function taking three arguments of the specified types, and returning int ([dcl.type]).  — end example ]

Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.

Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete (possibly cv-qualified) class type in the context of the function definition unless the function is deleted ([dcl.fct.def.delete]).

A typedef of function type may be used to declare a function but shall not be used to define a function ([dcl.fct.def]). [ Example:

typedef void F();
F  fv;              // OK: equivalent to void fv();
F  fv { }           // ill-formed
void fv() { }       // OK: definition of fv

 — end example ]

An identifier can optionally be provided as a parameter name; if present in a function definition ([dcl.fct.def]), it names a parameter. [ Note: In particular, parameter names are also optional in function definitions and names used for a parameter in different declarations and the definition of a function need not be the same. If a parameter name is present in a function declaration that is not a definition, it cannot be used outside of its function declarator because that is the extent of its potential scope ([basic.scope.proto]).  — end note ]

Example: the declaration

int i,
    *pi,
    f(),
    *fpi(int),
    (*pif)(const char*, const char*),
    (*fpif(int))(int);

declares an integer i, a pointer pi to an integer, a function f taking no arguments and returning an integer, a function fpi taking an integer argument and returning a pointer to an integer, a pointer pif to a function which takes two pointers to constant characters and returns an integer, a function fpif taking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer. It is especially useful to compare fpi and pif. The binding of *fpi(int) is *(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a function fpi, and then using indirection through the (pointer) result to yield an integer. In the declarator (*pif)(const char*, const char*), the extra parentheses are necessary to indicate that indirection through a pointer to a function yields a function, which is then called.  — end example ] [ Note: Typedefs and trailing-return-types are sometimes convenient when the return type of a function is complex. For example, the function fpif above could have been declared

typedef int  IFUNC(int);
IFUNC*  fpif(int);

or

auto fpif(int)->int(*)(int);

A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

rather than

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

 — end note ]

A non-template function is a function that is not a function template specialization. [ Note: A function template is not a function.  — end note ]

A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack ([temp.variadic]). When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack ([temp.variadic]). [ Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see [temp.param].  — end note ] A function parameter pack is a pack expansion ([temp.variadic]). [ Example:

template<typename... T> void f(T (* ...t)(int, int));

int add(int, int);
float subtract(int, int);

void g() {
  f(add, subtract);
}

 — end example ]

There is a syntactic ambiguity when an ellipsis occurs at the end of a parameter-declaration-clause without a preceding comma. In this case, the ellipsis is parsed as part of the abstract-declarator if the type of the parameter either names a template parameter pack that has not been expanded or contains auto; otherwise, it is parsed as part of the parameter-declaration-clause.101

As indicated by syntax, cv-qualifiers are a significant component in function return types.

One can explicitly disambiguate the parse either by introducing a comma (so the ellipsis will be parsed as part of the parameter-declaration-clause) or by introducing a name for the parameter (so the ellipsis will be parsed as part of the declarator-id).

8.3.6 Default arguments [dcl.fct.default]

If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.

Example: the declaration

void point(int = 3, int = 4);

declares a function that can be called with zero, one, or two arguments of type int. It can be called in any of these ways:

point(1,2);  point(1);  point();

The last two calls are equivalent to point(1,4) and point(3,4), respectively.  — end example ]

A default argument shall be specified only in the parameter-declaration-clause of a function declaration or lambda-declarator or in a template-parameter ([temp.param]); in the latter case, the initializer-clause shall be an assignment-expression. A default argument shall not be specified for a parameter pack. If it is specified in a parameter-declaration-clause, it shall not occur within a declarator or abstract-declarator of a parameter-declaration.102

For non-template functions, default arguments can be added in later declarations of a function in the same scope. Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa. In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack. A default argument shall not be redefined by a later declaration (not even to the same value). [ Example:

void g(int = 0, ...);           // OK, ellipsis is not a parameter so it can follow
                                // a parameter with a default argument
void f(int, int);
void f(int, int = 7);
void h() {
  f(3);                         // OK, calls f(3, 7)
  void f(int = 1, int);         // error: does not use default
                                // from surrounding scope
}
void m() {
  void f(int, int);             // has no defaults
  f(4);                         // error: wrong number of arguments
  void f(int, int = 5);         // OK
  f(4);                         // OK, calls f(4, 5);
  void f(int, int = 5);         // error: cannot redefine, even to
                                // same value
}
void n() {
  f(6);                         // OK, calls f(6, 7)
}

 — end example ] For a given inline function defined in different translation units, the accumulated sets of default arguments at the end of the translation units shall be the same; see [basic.def.odr]. If a friend declaration specifies a default argument expression, that declaration shall be a definition and shall be the only declaration of the function or function template in the translation unit.

The default argument has the same semantic constraints as the initializer in a declaration of a variable of the parameter type, using the copy-initialization semantics ([dcl.init]). The names in the default argument are bound, and the semantic constraints are checked, at the point where the default argument appears. Name lookup and checking of semantic constraints for default arguments in function templates and in member functions of class templates are performed as described in [temp.inst]. [ Example: in the following code, g will be called with the value f(2):

int a = 1;
int f(int);
int g(int x = f(a));            // default argument: f(::a)

void h() {
  a = 2;
  {
  int a = 3;
  g();                          // g(f(::a))
  }
}

 — end example ] [ Note: In member function declarations, names in default arguments are looked up as described in [basic.lookup.unqual]. Access checking applies to names in default arguments as described in Clause [class.access].  — end note ]

Except for member functions of class templates, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition; the program is ill-formed if a default constructor ([class.ctor]), copy or move constructor, or copy or move assignment operator ([class.copy]) is so declared. Default arguments for a member function of a class template shall be specified on the initial declaration of the member function within the class template. [ Example:

class C {
  void f(int i = 3);
  void g(int i, int j = 99);
};

void C::f(int i = 3) {          // error: default argument already
}                               // specified in class scope
void C::g(int i = 88, int j) {  // in this translation unit,
}                               // C::g can be called with no argument

 — end example ]

A local variable shall not appear as a potentially-evaluated expression in a default argument. [ Example:

void f() {
  int i;
  extern void g(int x = i);          // error
  extern void h(int x = sizeof(i));  // OK
  // ...
}

 — end example ]

Note: The keyword this may not appear in a default argument of a member function; see [expr.prim.this]. [ Example:

class A {
  void f(A* p = this) { }       // error
};

 — end example ]  — end note ]

A default argument is evaluated each time the function is called with no argument for the corresponding parameter. A parameter shall not appear as a potentially-evaluated expression in a default argument. Parameters of a function declared before a default argument are in scope and can hide namespace and class member names. [ Example:

int a;
int f(int a, int b = a);            // error: parameter a
                                    // used as default argument
typedef int I;
int g(float I, int b = I(2));       // error: parameter I found
int h(int a, int b = sizeof(a));    // OK, unevaluated operand

 — end example ] A non-static member shall not appear in a default argument unless it appears as the id-expression of a class member access expression ([expr.ref]) or unless it is used to form a pointer to member ([expr.unary.op]). [ Example: the declaration of X::mem1() in the following example is ill-formed because no object is supplied for the non-static member X::a used as an initializer.

int b;
class X {
  int a;
  int mem1(int i = a);          // error: non-static member a
                                // used as default argument
  int mem2(int i = b);          // OK;  use X::b
  static int b;
};

The declaration of X::mem2() is meaningful, however, since no object is needed to access the static member X::b. Classes, objects, and members are described in Clause [class].  — end example ] A default argument is not part of the type of a function. [ Example:

int f(int = 0);

void h() {
  int j = f(1);
  int k = f();                  // OK, means f(0)
}

int (*p1)(int) = &f;
int (*p2)() = &f;               // error: type mismatch

 — end example ] When a declaration of a function is introduced by way of a using-declaration ([namespace.udecl]), any default argument information associated with the declaration is made known as well. If the function is redeclared thereafter in the namespace with additional default arguments, the additional arguments are also known at any point following the redeclaration where the using-declaration is in scope.

A virtual function call ([class.virtual]) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides. [ Example:

struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m() {
  B* pb = new B;
  A* pa = pb;
  pa->f();          // OK, calls pa->B::f(7)
  pb->f();          // error: wrong number of arguments for B::f()
}

 — end example ]

This means that default arguments cannot appear, for example, in declarations of pointers to functions, references to functions, or typedef declarations.