84 KiB
[dcl.meaning]
9 Declarations [dcl]
9.3 Declarators [dcl.decl]
9.3.4 Meaning of declarators [dcl.meaning]
9.3.4.1 General [dcl.meaning.general]
A declarator contains exactly one declarator-id; it names the entity that is declared.
If the unqualified-id occurring in a declarator-id is a template-id, the declarator shall appear in the declaration of atemplate-declaration ([temp.decls]),explicit-specialization ([temp.expl.spec]), orexplicit-instantiation ([temp.explicit]).
[Note 1:
An unqualified-id that is not an identifier is used to declare certain functions ([class.conv.fct], [class.dtor], [over.oper], [over.literal]).
â end note]
The optional attribute-specifier-seq following a declarator-id appertains to the entity that is declared.
If the declaration is a friend declaration:
-
The declarator does not bind a name.
-
If the id-expression E in the declarator-id of the declarator is a qualified-id or a template-id:
-
If the friend declaration is not a template declaration, then in the lookup for the terminal name of E: + (2.2.1.1) if the unqualified-id in E is a template-id, all function declarations are discarded;
-
[(2.2.1.2)](#general-2.2.1.2)
-
-
otherwise, if the declarator corresponds ([basic.scope.scope]) to any declaration found of a non-template function, all function template declarations are discarded;
+
[(2.2.1.3)](#general-2.2.1.3)
each remaining function template is replaced with the specialization chosen by deduction from the friend declaration ([temp.deduct.decl]) or discarded if deduction fails.
-
The declarator shall correspond to one or more declarations found by the lookup; they shall all have the same target scope, and the target scope of the declarator is that scope.
-
Otherwise, the terminal name of E is not looked up. The declaration's target scope is the innermost enclosing namespace scope; if the declaration is contained by a block scope, the declaration shall correspond to a reachable ([module.reach]) declaration that inhabits the innermost block scope.
Otherwise:
-
If the id-expression in the declarator-id of the declarator is a qualified-id Q, let S be its lookup context ([basic.lookup.qual]); the declaration shall inhabit a namespace scope.
-
Otherwise, let S be the entity associated with the scope inhabited by the declarator.
-
If the declarator declares an explicit instantiation or a partial or explicit specialization, the declarator does not bind a name. If it declares a class member, the terminal name of the declarator-id is not looked up; otherwise, only those lookup results that are nominable in S are considered when identifying any function template specialization being declared ([temp.deduct.decl]). [Example 1: namespace N {inline namespace O {template void f(T); // #1template void g(T) {}}namespace P {template void f(T*); // #2, more specialized than #1template int g; }using P::f,P::g;}template<> void N::f(int*) {} // OK, #2 is not nominabletemplate void N::g(int); // error: lookup is ambiguous â end example]
-
Otherwise, the terminal name of the declarator-id is not looked up. If it is a qualified name, the declarator shall correspond to one or more declarations nominable in S; all the declarations shall have the same target scope and the target scope of the declarator is that scope. [Example 2: namespace Q {namespace V {void f(); }void V::f() { /* ... / } // OKvoid V::g() { / ... / } // error: g() is not yet a member of Vnamespace V {void g(); }}namespace R {void Q::V::g() { / ... */ } // error: R doesn't enclose Q} â end example]
-
If the declaration inhabits a block scope S and declares a function ([dcl.fct]) or uses the extern specifier, the declaration shall not be attached to a named module ([module.unit]); its target scope is the innermost enclosing namespace scope, but the name is bound in S. [Example 3: namespace X {void p() { q(); // error: q not yet declaredextern void q(); // q is a member of namespace Xextern void r(); // r is a member of namespace X}void middle() { q(); // error: q not found}void q() { /* ... / } // definition of X::q}void q() { / ... / } // some other, unrelated qvoid X::r() { / ... */ } // error: r cannot be declared by qualified-id â end example]
Astatic,thread_local,extern,mutable,friend,inline,virtual,constexpr,consteval,constinit, ortypedef specifier or an explicit-specifier applies directly to each declarator-id in a declaration; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.
Thus, (for each declarator) a declaration has the formT D whereT is of the form attribute-specifier-seqoptdecl-specifier-seq andD is a declarator.
Following is a recursive procedure for determining the type specified for the containeddeclarator-id by such a declaration.
First, thedecl-specifier-seq determines a type.
In a declarationT D thedecl-specifier-seqT determines the typeT.
[Example 4:
In the declarationint unsigned i; the type specifiersintunsigned determine the type âunsigned intâ ([dcl.type.simple]).
â end example]
In a declarationattribute-specifier-seqoptTD whereD is an unadorned declarator-id, the type of the declared entity is âTâ.
In a declarationTD whereD has the form
( D1 )
the type of the containeddeclarator-id is the same as that of the containeddeclarator-id in the declarationT D1
Parentheses do not alter the type of the embeddeddeclarator-id, but they can alter the binding of complex declarators.
9.3.4.2 Pointers [dcl.ptr]
In a declarationTD whereD has the form
- attribute-specifier-seqopt cv-qualifier-seqopt D1
and the type of the contained declarator-id in the declarationTD1 is âderived-declarator-type-listTâ, the type of the declarator-id inD is âderived-declarator-type-list cv-qualifier-seq pointer toTâ.
Thecv-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 1:
The declarationsconst int ci = 10, *pc = &ci, *const cpc = pc, **ppc;int i, *p, *const cp = &i; declareci, 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; andcp, a constant pointer to integer.
The value ofci,cpc, andcp cannot be changed after initialization.
The value ofpc can be changed, and so can the object pointed to bycp.
Examples of some correct operations arei = ci;*cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;
Examples of ill-formed operations areci = 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 declaredconst 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 errorp = 5; // clobber ci
â end example]
See also [expr.assign] and [dcl.init].
[Note 1:
Forming a pointer to reference type is ill-formed; see [dcl.ref].
Forming a function pointer type is ill-formed if the function type hascv-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]
9.3.4.3 References [dcl.ref]
In a declarationTD whereD has either of the forms
& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1
and the type of the contained declarator-id in the declarationTD1 is âderived-declarator-type-listTâ, the type of the declarator-id inD is âderived-declarator-type-list reference toTâ.
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 atypedef-name ([dcl.typedef], [temp.param]) ordecltype-specifier ([dcl.type.decltype]), in which case the cv-qualifiers are ignored.
[Example 1: typedef int& A;const A aref = 3; // error: lvalue reference to non-const initialized with rvalue
The type ofaref is âlvalue reference to intâ, not âlvalue reference to const intâ.
â end example]
[Note 1:
A reference can be thought of as a name of an object.
â end note]
Forming the type âreference to cv voidâ is ill-formed.
A reference type that is declared using & is called anlvalue reference, and a reference type that is declared using && is called anrvalue reference.
Lvalue references and rvalue references are distinct types.
Except where explicitly noted, they are semantically equivalent and commonly referred to as references.
[Example 2:
void f(double& a) { a += 3.14; }// ...double d = 0; f(d); declaresa to be a reference parameter off so the callf(d) will add3.14 tod.
int v[20];// ...int& g(int i) { return v[i]; }// ... g(3) = 7; declares the functiong() to return a reference to an integer sog(3)=7 will assign7 to the fourth element of the arrayv.
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);} declaresp to be a reference to a pointer tolink soh(q) will leaveq 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 aninitializer ([dcl.init.ref]) except when the declaration contains an explicitextern 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].
Attempting to bind a reference to a function where the converted initializer is a glvalue whose type is not call-compatible ([expr.call]) with the type of the function's definition results in undefined behavior.
Attempting to bind a reference to an object where the converted initializer is a glvalue through which the object is not type-accessible ([basic.lval]) results in undefined behavior.
[Note 2:
The object designated by such a glvalue can be outside its lifetime ([basic.life]).
Because a null pointer value or a pointer past the end of an object does not point to an object, a reference in a well-defined program cannot refer to such things; see [expr.unary.op].
As described in [class.bit], a reference cannot be bound directly to a bit-field.
â end note]
The behavior of an evaluation of a reference ([expr.prim.id], [expr.ref]) that does not happen after ([intro.races]) the initialization of the reference is undefined.
[Example 3: int &f(int&);int &g();extern int &ir3;int *ip = 0;int &ir1 = *ip; // undefined behavior: null pointerint &ir2 = f(ir3); // undefined behavior: ir3 not yet initializedint &ir3 = g();int &ir4 = f(ir4); // undefined behavior: ir4 used in its own initializerchar x alignas(int);int &ir5 = *reinterpret_cast<int *>(&x); // undefined behavior: initializer refers to char object â end example]
If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.decltype]) 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 3:
This rule is known as reference collapsing.
â end note]
[Example 4: 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 4:
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]
9.3.4.4 Pointers to members [dcl.mptr]
The component names of a ptr-operator are those of its nested-name-specifier, if any.
In a declarationTD whereD has the form
nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1
and thenested-name-specifier designates a class, and the type of the contained declarator-id in the declarationTD1 is âderived-declarator-type-listTâ, the type of the declarator-id inD is âderived-declarator-type-list cv-qualifier-seq pointer to member of classnested-name-specifier of typeTâ.
The optional attribute-specifier-seq ([dcl.attr.grammar]) appertains to the pointer-to-member.
The nested-name-specifier shall not designate an anonymous union.
[Example 1:
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; declarespmi,pmf,pmd andpmc to be a pointer to a member ofX of typeint, a pointer to a member ofX of typevoid(int), a pointer to a member ofX of typedouble and a pointer to a member ofY of typechar respectively.
The declaration ofpmd is well-formed even thoughX has no members of typedouble.
Similarly, the declaration ofpmc is well-formed even thoughY is an incomplete type.
pmi andpmf 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 1:
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]
9.3.4.5 Arrays [dcl.array]
In a declaration T D where D has the form
D1 [ constant-expressionopt ] 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 array of N Tâ.
The constant-expression shall be a converted constant expression of type std::size_t ([expr.const]).
Its value N specifies the array bound, i.e., the number of elements in the array;N shall be greater than zero.
In a declaration T D where D has the form
D1 [ ] 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 array of unknown bound of Tâ, except as specified below.
A type of the form âarray of N Uâ or âarray of unknown bound of Uâ is an array type.
The optional attribute-specifier-seq appertains to the array type.
U is called the array element type; this type shall not be a reference type, a function type, an array of unknown bound, orcv void.
[Note 1:
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 an array of known bound.
â end note]
[Example 1:
float fa[17], *afp[17]; declares an array of float numbers and an array of pointers to float numbers.
â end example]
Any type of the form âcv-qualifier-seq array of N Uâ is adjusted to âarray of N cv-qualifier-seq Uâ, and similarly for âarray of unknown bound of Uâ.
[Example 2: typedef int A[5], AA[2][3];typedef const A CA; // type is âarray of 5 const int''typedef const AA CAA; // type is âarray of 2 array of 3 const int'' â end example]
[Note 2:
An âarray of N cv-qualifier-seq Uâ has cv-qualified type; see [basic.type.qualifier].
â end note]
An object of type âarray of N Uâ consists of a contiguously allocated non-empty set of N subobjects of type U, known as the elements of the array, and numbered 0 to N-1.
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 an object (but not a non-static data member) of array type is initialized and the declarator is followed by an initializer ([dcl.init], [class.mem], [expr.type.conv], [expr.new]).
In these cases, the array bound is calculated from the number of initial elements (say, N) supplied ([dcl.init.aggr]), and the type of the array is âarray of N Uâ.
Furthermore, if there is a reachable declaration of the entity that specifies a bound and has the same host scope ([basic.scope.scope]), 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 3: extern int x[10];struct S {static int y[10];};
int x[]; // OK, bound is 10int S::y[]; // OK, bound is 10void f() {extern int x[]; int i = sizeof(x); // error: incomplete object type}namespace A { extern int z[3]; }int A::z[] = {}; // OK, defines an array of 3 elements â end example]
[Note 3:
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 can be omitted.
[Example 4:
int x3d[3][5][7]; declares an array of three elements, each of which is an array of five elements, each of which is an array of seven integers.
The overall array can be viewed as a three-dimensional array of integers, with rank 3 ×5 ×7.
Any of the expressionsx3d,x3d[i],x3d[i][j],x3d[i][j][k] can reasonably appear in an expression.
The expressionx3d[i] is equivalent to*(x3d + i); in that expression,x3d is subject to the array-to-pointer conversion ([conv.array]) and is first converted to a pointer to a 2-dimensional array with rank5 ×7 that points to the first element of x3d.
Then i is added, which on typical implementations involves multiplyingi by the length of the object to which the pointer points, which is sizeof(int)×5 ×7.
The result of the addition and indirection is an lvalue denoting the ith array element ofx3d (an array of five arrays of seven integers).
If there is another subscript, the same argument applies again, sox3d[i][j] is an lvalue denoting the jth array element of the ith array element ofx3d (an array of seven integers), andx3d[i][j][k] is an lvalue denoting the kth array element of the jth array element of the ith array element ofx3d (an integer).
â end example]
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]
[Note 4:
Conversions affecting expressions of array type are described in [conv.array].
â end note]
[Note 5:
The subscript operator can be overloaded for a class ([over.sub]).
For the operator's built-in meaning, see [expr.sub].
â end note]
9.3.4.6 Functions [dcl.fct]
In a declarationTD whereT may be empty andD has the form
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt
a derived-declarator-type-list is determined as follows:
-
If the unqualified-id of the declarator-id is a conversion-function-id, the derived-declarator-type-list is empty.
-
Otherwise, the derived-declarator-type-list is as appears in the type âderived-declarator-type-list Tâ of the containeddeclarator-id in the declarationTD1.
The declared return type U of the function type is determined as follows:
-
If the trailing-return-type is present,T shall be the single type-specifier auto, andU is the type specified by the trailing-return-type.
-
Otherwise, if the declaration declares a conversion function, see [class.conv.fct].
-
Otherwise, U is T.
The type of thedeclarator-id inD is âderived-declarator-type-listnoexceptopt function of parameter-type-listcv-qualifier-seqopt ref-qualifieropt returning Uâ, where
the parameter-type-list is derived from the parameter-declaration-clause as described below and
the optional noexcept is present if and only if the exception specification ([except.spec]) is non-throwing.
Such a type is a function type.75
The optional attribute-specifier-seq appertains to the function type.
parameter-declaration-clause:
...
parameter-declaration-listopt
parameter-declaration-list , ...
parameter-declaration-list ...
parameter-declaration-list:
parameter-declaration
parameter-declaration-list , parameter-declaration
parameter-declaration:
attribute-specifier-seqopt thisopt decl-specifier-seq declarator
attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
attribute-specifier-seqopt thisopt decl-specifier-seq abstract-declaratoropt
attribute-specifier-seqopt decl-specifier-seq abstract-declaratoropt = initializer-clause
The optional attribute-specifier-seq in a parameter-declaration appertains to the parameter.
Theparameter-declaration-clause determines the arguments that can be specified, and their processing, when the function is called.
[Note 1:
Theparameter-declaration-clause is used to convert the arguments specified on the function call; see [expr.call].
â end note]
If theparameter-declaration-clause is empty, the function takes no arguments.
A parameter list consisting of a single unnamed non-object 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.
A parameter with volatile-qualified type is deprecated; see [depr.volatile.type].
If theparameter-declaration-clauseterminates 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 â, ...â.
A parameter-declaration-clause of the formparameter-declaration-list ... is deprecated ([depr.ellipsis.comma]).
[Example 1:
The declarationint 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 aconstchar*.
â end example]
[Note 2:
The standard header contains a mechanism for accessing arguments passed using the ellipsis (see [expr.call] and [support.runtime]).
â end note]
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 parameter-declaration ([dcl.decl]).
After determining the type of each parameter, any parameterof type âarray of Tâ orof function type T is adjusted to be âpointer to Tâ.
After producing the list of parameter types, any top-levelcv-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'sparameter-type-list.
[Note 3:
This transformation does not affect the types of the parameters.
For example, int()(const int p, decltype(p)) andint()(int, const int) are identical types.
â end note]
[Example 2: void f(char*); // #1void f(char[]) {} // defines #1void f(const char*) {} // OK, another overloadvoid f(char const) {} // error: redefines #1void g(char()[2]); // #2void g(char[3][2]) {} // defines #2void g(char[3][3]) {} // OK, another overloadvoid h(int x(const int)); // #3void h(int (*)(int)) {} // defines #3 â end example]
An explicit-object-parameter-declaration is a parameter-declaration with a this specifier.
An explicit-object-parameter-declaration shall appear only as the first parameter-declaration of a parameter-declaration-list of one of:
a declaration of a member function or member function template ([class.mem]), or
an explicit instantiation ([temp.explicit]) or explicit specialization ([temp.expl.spec]) of a templated member function, or
a lambda-declarator ([expr.prim.lambda]).
A member-declarator with an explicit-object-parameter-declaration shall not include a ref-qualifier or a cv-qualifier-seq and shall not be declared static or virtual.
[Example 3: struct C {void f(this C& self); template void g(this Self&& self, int); void h(this C) const; // error: const not allowed here};
void test(C c) { c.f(); // OK, calls C::f c.g(42); // OK, calls C::g<C&> std::move(c).g(42); // OK, calls C::g} â end example]
A function parameter declared with an explicit-object-parameter-declaration is an explicit object parameter.
An explicit object parameter shall not be a function parameter pack ([temp.variadic]).
An explicit object member function is a non-static member function with an explicit object parameter.
An implicit object member function is a non-static member function without an explicit object parameter.
The object parameter of a non-static member function is either the explicit object parameter or the implicit object parameter ([over.match.funcs]).
A non-object parameter is a function parameter that is not the explicit object parameter.
The non-object-parameter-type-list of a member function is the parameter-type-list of that function with the explicit object parameter, if any, omitted.
[Note 4:
The non-object-parameter-type-list consists of the adjusted types of all the non-object parameters.
â end note]
A function type with a cv-qualifier-seq or aref-qualifier (including a type denoted bytypedef-name ([dcl.typedef], [temp.param])) shall appear only as:
the function type for a non-static member function,
the function type to which a pointer to member refers,
the top-level function type of a function typedef declaration or alias-declaration,
the type-id in the default argument of atype-parameter ([temp.param]),
the type-id of a template-argument for atype-parameter ([temp.arg.type]), or
the operand of a reflect-expression ([expr.reflect]).
[Example 4: typedef int FIC(int) const; FIC f; // error: does not declare a member functionstruct S { FIC f; // OK}; FIC S::*pm = &S::f; // OKconstexpr std::meta::info yeti = ^^void(int) const &; // OK â end example]
The effect of acv-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 5:
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 5: 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]) or the trailing requires-clause ([dcl.decl]), are part of the function type.
[Note 6:
Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions.
â end note]
[Example 6:
The declarationint fseek(FILE*, long, int); declares a function taking three arguments of the specified types, and returningint ([dcl.type]).
â end example]
[Note 7:
A single name can be used for several different functions in a single scope; this is function overloading ([over]).
â end note]
The return type shall be a non-array object type, a reference type, or cv void.
[Note 8:
An array of placeholder type is considered an array type.
â end note]
A volatile-qualified return type is deprecated; see [depr.volatile.type].
Types shall not be defined in return or parameter types.
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 7: typedef void F(); F fv; // OK, equivalent to void fv(); F fv { } // errorvoid 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 9:
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.
â end note]
[Example 8:
The declarationint i, *pi, f(), fpi(int), (pif)(const char, const char), (*fpif(int))(int); declares an integeri, a pointerpi to an integer, a functionf taking no arguments and returning an integer, a functionfpi taking an integer argument and returning a pointer to an integer, a pointerpif to a function which takes two pointers to constant characters and returns an integer, a functionfpif 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 comparefpi andpif.
The binding offpi(int) is(fpi(int)), so the declaration suggests, and the same construction in an expression requires, the calling of a functionfpi, 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 10:
Typedefs and trailing-return-types are sometimes convenient when the return type of a function is complex.
For example, the functionfpif above can be declaredtypedef int IFUNC(int); IFUNC* fpif(int); orauto 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 thantemplate <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 11:
A function template is not a function.
â end note]
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]).
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type-parameter for each generic parameter type placeholder of the function declaration, in order of appearance.
For a placeholder-type-specifier of the form auto, the invented parameter is an unconstrained type-parameter.
For a placeholder-type-specifier of the formtype-constraint auto, the invented parameter is a type-parameter with that type-constraint.
The invented type-parameter declares a template parameter pack if the corresponding parameter-declaration declares a function parameter pack.
If the placeholder contains decltype(auto), the program is ill-formed.
The adjusted function parameters of an abbreviated function template are derived from the parameter-declaration-clause by replacing each occurrence of a placeholder with the name of the corresponding invented type-parameter.
[Example 9: template concept C1 = /* ... /;template concept C2 = / ... /;template<typename... Ts> concept C3 = / ... */;
void g1(const C1 auto*, C2 auto&);void g2(C1 auto&...);void g3(C3 auto...);void g4(C3 auto);
The declarations above are functionally equivalent (but not equivalent) to their respective declarations below:template<C1 T, C2 U> void g1(const T*, U&);template<C1... Ts> void g2(Ts&...);template<C3... Ts> void g3(Ts...);template void g4(T);
Abbreviated function templates can be specialized like all function templates.
template<> void g1(const int*, const double&); // OK, specialization of g1<int, const double> â end example]
An abbreviated function template can have a template-head.
The invented type-parameters are appended to the template-parameter-list after the explicitly declared template-parameters.
[Example 10: template concept C = /* ... */;
template <typename T, C U>void g(T x, U y, C auto z);
This is functionally equivalent to each of the following two declarations.
template<typename T, C U, C W>void g(T x, U y, W z);
template<typename T, typename U, typename W>requires C && Cvoid g(T x, U y, W z); â end example]
A function declaration at block scope shall not declare an abbreviated function template.
A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration.
When it is part of aparameter-declaration-clause, the parameter-declaration declares a function parameter pack ([temp.variadic]).
Otherwise, the parameter-declaration is part of atemplate-parameter-list and declares a template parameter pack; see [temp.param].
A function parameter pack is a pack expansion ([temp.variadic]).
[Example 11: 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 theabstract-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.76
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 theparameter-declaration-clause) or by introducing a name for the parameter (so the ellipsis will be parsed as part of thedeclarator-id).
9.3.4.7 Default arguments [dcl.fct.default]
If an initializer-clause is specified in aparameter-declaration thisinitializer-clause is used as a default argument.
[Note 1:
Default arguments will be used in calls where trailing arguments are missing ([expr.call]).
â end note]
[Example 1:
The declarationvoid point(int = 3, int = 4); declares a function that can be called with zero, one, or two arguments of typeint.
It can be called in any of these ways:point(1,2); point(1); point();
The last two calls are equivalent topoint(1,4) andpoint(3,4), respectively.
â end example]
A default argument shall be specified only in theparameter-declaration-clause of a function declaration or lambda-declarator or in atemplate-parameter ([temp.param]).
A default argument shall not be specified for a template parameter pack or a function parameter pack.
If it is specified in aparameter-declaration-clause, it shall not occur within adeclarator orabstract-declarator of aparameter-declaration.77
For non-template functions, default arguments can be added in later declarations of a function that have the same host scope.
Declarations that have different host 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, unless the parameter was expanded from a parameter pack, or shall be a function parameter pack.
[Note 2:
A default argument cannot be redefined by a later declaration (not even to the same value) ([basic.def.odr]).
â end note]
[Example 2: void g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow// a parameter with a default argumentvoid 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 argumentsvoid 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)}template<class ... T> struct C {void f(int n = 0, T...);}; C c; // OK, instantiates declaration void C::f(int n = 0, int) â 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; no diagnostic is required.
If a friend declaration D specifies a default argument expression, that declaration shall be a definition and there shall be no other declaration of the function or function template which is reachable from D or from which D is reachable.
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 looked up, and the semantic constraints are checked, at the point where the default argument appears, except that an immediate invocation ([expr.const]) that is a potentially-evaluated subexpression ([intro.execution]) of the initializer-clause in a parameter-declaration is neither evaluated nor checked for whether it is a constant expression at that point.
Name lookup and checking of semantic constraints for default arguments of templated functions are performed as described in [temp.inst].
[Example 3:
In the following code,g will be called with the valuef(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 3:
A default argument is a complete-class context ([class.mem]).
Access checking applies to names in default arguments as described in [class.access].
â end note]
Except for member functions of templated classes, 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.default.ctor]), copy or move constructor ([class.copy.ctor]), or copy or move assignment operator ([class.copy.assign]) is so declared.
Default arguments for a member function of a templated class shall be specified on the initial declaration of the member function within the templated class.
[Example 4: 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 scopevoid C::g(int i = 88, int j) {} // in this translation unit, C::g can be called with no arguments â end example]
[Note 4:
A local variable cannot be odr-used ([basic.def.odr]) in a default argument.
â end note]
[Example 5: void f() {int i; extern void g(int x = i); // errorextern void h(int x = sizeof(i)); // OK// ...} â end example]
[Note 5:
The keywordthis cannot appear in a default argument of a member function; see [expr.prim.this].
[Example 6: 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.
[Note 6:
Parameters of a function declared before a default argument are in scope and can hide namespace and class member names.
â end note]
[Example 7: int a;int f(int a, int b = a); // error: parameter a used as default argumenttypedef int I;int g(float I, int b = I(2)); // error: parameter I foundint h(int a, int b = sizeof(a)); // OK, unevaluated operand â end example]
A non-static member shall not be designated in a default argument unless
it is designated by the id-expression or splice-expression of a class member access expression ([expr.ref]),
it is designated by an expression used to form a pointer to member ([expr.unary.op]), or
it appears as the operand of a reflect-expression ([expr.reflect]).
[Example 8:
The declaration ofX::mem1() in the following example is ill-formed because no object is supplied for the non-static memberX::a used as an initializer.
int b;class X {int a; int mem1(int i = a); // error: non-static member a used as default argumentint mem2(int i = b); // OK, use X::bconsteval void mem3(std::meta::info r = ^^a) {} // OKint mem4(int i = [:^^a:]); // error: non-static member a designated in default argumentstatic int b;};
The declaration ofX::mem2() is meaningful, however, since no object is needed to access the static memberX::b.
Classes, objects, and members are described in [class].
â end example]
A default argument is not part of the type of a function.
[Example 9: 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]
[Note 7:
When an overload set contains a declaration of a function whose host scope is S, any default argument associated with any reachable declaration whose host scope is S is available to the call ([over.match.viable]).
â end note]
[Note 8:
The candidate might have been found through a using-declarator from which the declaration that provides the default argument is not reachable.
â end note]
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 10: 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, ortypedef declarations.