-1- A declarator declares a single object, function, or type, within a declaration. The init-declarator-list appearing in a declaration is a comma-separated sequence of declarators, each of which can have an initializer.
init-declarator-list: init-declarator init-declarator-list , init-declarator
init-declarator: declarator initializeropt
-2- The two components of a declaration are the specifiers (decl-specifier-seq; dcl.spec) and the declarators (init-declarator-list). The specifiers indicate the type, storage class or other properties of the objects, functions or typedefs being declared. The declarators specify the names of these objects, functions or typedefs, and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning). Initial values can also be specified in a declarator; initializers are discussed in dcl.init and class.init.
-3- Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.*
[Footnote: A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That isis usually equvalent toT D1, D2, ... Dn;where T is a decl-specifier-seq and each Di is a init-declarator. The exception occurs when a name introduced by one of the declarators hides a type name used by the dcl-specifiers, so that when the same dcl-specifiers are used in a subsequent declaration, they do not have the same meaning, as inT D1; T D2; ... T Dn;which is not equivalent tostruct S { ... }; S S, T; //declare two instances of struct S--- end foonote]struct S { ... }; S S; S T; //error
-4- Declarators have the syntax
declarator: direct-declarator ptr-operator declarator
direct-declarator: declarator-id direct-declarator ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt direct-declarator [ constant-expressionopt ] ( declarator )
ptr-operator: * cv-qualifier-seqopt & ::opt nested-name-specifier * cv-qualifier-seqopt
cv-qualifier-seq: cv-qualifier cv-qualifier-seqopt
cv-qualifier: const volatile
A class-name has special meaning in a declaration of the class of that name and when qualified by that name using the scope resolution operator :: (expr.prim, class.ctor, class.dtor).declarator-id: id-expression ::opt nested-name-specifieropt type-name
-1- To specify type conversions explicitly, and as an argument of sizeof, new, or typeid, the name of a type shall be specified. This can be done with a type-id, which is syntactically a declaration for an object or function of that type that omits the name of the object or function.
type-id: type-specifier-seq abstract-declaratoropt
type-specifier-seq: type-specifier type-specifier-seqopt
abstract-declarator: ptr-operator abstract-declaratoropt direct-abstract-declarator
It is possible to identify uniquely the location in the abstract-declarator where the identifier would appear if the construction were a declarator in a declaration. The named type is then the same as the type of the hypothetical identifier. [Example:direct-abstract-declarator: direct-abstract-declaratoropt ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt direct-abstract-declaratoropt [ constant-expressionopt ] ( abstract-declarator )
name respectively the types ``int,'' ``pointer to int,'' ``array of 3 pointers to int,'' ``pointer to array of 3 int,'' ``function of (no parameters) returning pointer to int,'' and ``pointer to a function of (double) returning int.'' ]int //int i int * // int *pi int *[3] // int *p[3] int (*)[3] // int (*p3i)[3] int *() // int *f() int (*)(double) // int (*pf)(double)
-2-
A type can also be named (often more easily) by using a
typedef
(dcl.typedef).
8.2 - Ambiguity resolution [dcl.ambig.res]
-1- The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in stmt.ambig can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in stmt.ambig, the resolution is to consider any construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ] [Example:
struct S { S(int); };
void foo(double a) { S w(int(a)); //function declaration S x(int()); // function declaration S y((int)a); // object declaration S z = int(a); // object declaration }
-2- The ambiguity arising from the similarity between a function-style cast and a type-id can occur in different contexts. The ambiguity appears as a choice between a function-style cast expression and a declaration of a type. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
-3- [Example:
#include <cstddef> char *p; void *operator new(size_t, int); void foo() { const int x = 63; new (int(*p)) int; //new-placement expression new (int(*[x])); // new type-id }
-4- For another example,
template <class T> struct S { T *p; }; S<int()> x; //type-id S<int(1)> y; // expression (ill-formed)
-5- For another example,
void foo() { sizeof(int(1)); //expression sizeof(int()); // type-id (ill-formed) }
-6- For another example,
void foo() { (int(1)); //expression (int())1; // type-id (ill-formed) }
-7- Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a simple-type-specifier rather than a declarator-id. [Example:
For another example,class C { }; void f(int(C)) { } //void f(int (*fp)(C c)) { } // not: void f(int C); int g(C); void foo() { f(1); // error: cannot convert 1 to function pointer f(g); // OK }
class C { }; void h(int *(C[10])); //void h(int *(*_fp)(C _parm[10])); // not: void h(int *C[10]);
-1- A list of declarators appears after an optional (clause dcl.dcl) decl-specifier-seq (dcl.spec). Each declarator contains exactly one declarator-id; it names the identifier that is declared. The id-expression of a declarator-id shall be a simple identifier except for the declaration of some special functions (class.conv, class.dtor, over.oper) and for the declaration of template specializations or partial specializations (temp.spec). A declarator-id shall not be qualified except for the definition of a member function (class.mfct) or static data member (class.static) or nested class (class.nest) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or the definition of a previously declared explicit specialization outside of its namespace, or the declaration of a friend function that is a member of another class or namespace (class.friend). 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, and the member shall not 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. [Note: if the qualifier is the global :: scope resolution operator, the declarator-id refers to a name declared in the global namespace scope. ] In the qualified declarator-id for a class or namespace member definition that appears outside of the member's class or namespace, the nested-name-specifier shall not name any of the namespaces that enclose the member's definition. [Example:
namespace A { struct B { void f(); }; void A::B::f() { } //ill-formed: the declarator must not be // qualified with A:: }
-2- An auto, static, extern, register, mutable, friend, inline, virtual, or typedef specifier applies directly to each declarator-id in a init-declarator-list; the type specified for each declarator-id depends on both the decl-specifier-seq and its declarator.
-3- Thus, a declaration of a particular identifier has the form
where T is a 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.T D
-4- First, the decl-specifier-seq determines a type. In a declaration
the decl-specifier-seq T determines the type ``T.'' [Example: in the declarationT D
the type specifiers int unsigned determine the type ``"unsigned int"'' (dcl.type.simple). ]int unsigned i;
-5- In a declaration T D where D is an unadorned identifier the type of this identifier is ``T.''
-6- In a declaration T D where D has the form
the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration( D1 )
Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.T D1
-1- In a declaration T D where D has the form
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.*cv-qualifier-seqopt D1
-2- [Example: the declarations
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 areconst int ci = 10, *pc = &ci, *const cpc = pc, **ppc; int i, *p, *const cp = &i;
Examples of ill-formed operations arei = ci; *cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;
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:ci = 1; //error ci++; // error *pc = 2; // error cp = &ci; // error cpc++; // error p = pc; // error ppc = &p; // error
*ppc = &ci; //OK, but would make p point to ci ... // ... because of previous error *p = 5; // clobber ci
-3- See also expr.ass and dcl.init.
-4-
[Note:
there are no pointers to references; see dcl.ref.
Since the address of a bit-field (class.bit) cannot be taken,
a pointer can never point to a bit-field.
]
8.3.2 - References [dcl.ref]
-1- In a declaration T D where D has the form
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.'' Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (dcl.typedef) or of a template type argument (temp.arg), in which case the cv-qualifiers are ignored. [Example: in& D1
the type of aref is ``reference to int'', not ``const reference to int''. ] [Note: a reference can be thought of as a name of an object. ] A declarator that specifies the type ``reference to cv void'' is ill-formed.typedef int& A; const A aref = 3; //ill-formed; // non-const reference initialized with rvalue
-2- [Example:
declares a to be a reference parameter of f so the call f(d) will add 3.14 to d.void f(double& a) { a += 3.14; } //... double d = 0; f(d);
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,int v[20]; //... int& g(int i) { return v[i]; } // ... g(3) = 7;
struct link { link* next; }; link* first;
void h(link*& p) //p is a reference to pointer { p->next = first; first = p; p = 0; }
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. ]void k() { link* q = new link; h(q); }
-3- It is unspecified whether or not a reference requires storage (basic.stc).
-4-
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 declaration,
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 dereferencing a null pointer,
which causes undefined behavior.
As described in class.bit, a reference cannot be bound directly
to a bit-field.
]
8.3.3 - Pointers to members [dcl.mptr]
-1- In a declaration T D where D has the form
and the nested-name-specifier names 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.''::opt nested-name-specifier * cv-qualifier-seqopt D1
-2- [Example:
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:class X { public: void f(int); int a; }; class Y; int X::* pmi = &X::a; void (X::* pmf)(int) = &X::f; double X::* pmd; char Y::* pmc;
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
-3-
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++.
]
8.3.4 - Arrays [dcl.array]
-1- In a declaration T D where D has the form
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. 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 an integral constant expression 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 sub-objects of type T. 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.'' [Example:D1 [constant-expressionopt]
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''
-2- 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.
-3- When several ``array of'' specifications are adjacent, a multidimensional array is created; the constant expressions that specify the bounds of the arrays can be omitted only for the first member of the sequence. [Note: this elision is useful for function parameters of array types, and when the array is external and the definition, which allocates storage, is given elsewhere. ] The first constant-expression can also be omitted when the declarator is followed by an initializer (dcl.init). In this case 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.''
-4- [Example:
declares an array of float numbers and an array of pointers to float numbers. For another example,float fa[17], *afp[17];
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. ]static int x3d[3][5][7];
-5- [Note: conversions affecting lvalues of array type are described in conv.array. Objects of array types cannot be modified, see basic.lval. ]
-6- 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.
-7-
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 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.
-8- [Example: consider
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. ]int x[3][5];
-9-
[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.
]
8.3.5 - Functions [dcl.fct]
-1- In a declaration T D where D has the form
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 function of (parameter-declaration-clause) cv-qualifier-seqopt returning T''; a type of this form is a function type*.D1 (parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt
[Footnote: As indicated by the syntax, cv-qualifiers are a significant component in function return types. --- end foonote]
parameter-declaration-clause: parameter-declaration-listopt ...opt parameter-declaration-list , ...
parameter-declaration-list: parameter-declaration parameter-declaration-list , parameter-declaration
parameter-declaration: decl-specifier-seq declarator decl-specifier-seq declarator = assignment-expression decl-specifier-seq abstract-declaratoropt decl-specifier-seq abstract-declaratoropt = assignment-expression
-2- 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. ] If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list. Except for this special case, void shall not be a parameter type (though types derived from void, such as void*, can). If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parameters specified. Where syntactically correct, ``, ...'' is synonymous with ``...''. [Example: the declaration
declares a function that can be called with varying numbers and types of arguments.int printf(const char*, ...);
However, the first argument must be of a type that can be converted to a const char*. ] [Note: the standard header <cstdarg> contains a mechanism for accessing arguments passed using the ellipsis (see expr.call and lib.support.runtime). ]printf("hello world"); printf("a=%d b=%d", a, b);
-3-
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 with a given parameter list
shall agree exactly both in the type of the value
returned and in the number and type of parameters;
the presence or absence of the ellipsis is considered part
of the function type.
The type of a function is determined using the following rules.
The type of each parameter 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 ``function returning
T''
is adjusted to be ``pointer to
T''
or ``pointer to function returning
T,''
respectively.
After producing the list of parameter types, several transformations take
place upon these types to determine the function type.
Any
cv-qualifier
modifying a parameter type is deleted.
[Example:
the type
void(*)(const
int)
becomes
void(*)(int)
--- end example]
Such
cv-qualifiers
affect only the definition of the parameter
within the body of the function;
they do not affect the function type.
If a
storage-class-specifier
modifies a parameter type, the specifier is deleted.
[Example:
register
char*
becomes
char*
--- end example]
Such
storage-class-specifiers
affect only the definition of the parameter
within the body of the function;
they do not affect the function type.
The resulting list of transformed parameter types is the
function's
parameter
type
list.
-4- A cv-qualifier-seq shall only be part of the function type for a nonstatic member function, the function type to which a pointer to member refers, or the top-level function type of a function typedef declaration. 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, i.e., it does not create a cv-qualified function type. In fact, if at any time in the determination of a type a cv-qualified function type is formed, the program is ill-formed. [Example:
typedef void F(); struct S { const F f; //ill-formed: // not equivalent to: void f() const; };
-5- [Example: the declaration
declares a function taking three arguments of the specified types, and returning int (dcl.type). ]int fseek(FILE*, long, int);
-6- If the type of a parameter includes a type of the form ``pointer to array of unknown bound of T'' or ``reference to array of unknown bound of T,'' the program is ill-formed.*
[Footnote: This excludes parameters of type ``ptr-arr-seq T2'' where T2 is ``pointer to array of unknown bound of T'' and where ptr-arr-seq means any sequence of ``pointer to'' and ``array of'' derived declarator types. This exclusion applies to the parameters of the function, and if a parameter is a pointer to function or pointer to member function then to its parameters also, etc. --- end foonote]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 declaration that is not a definition may be an incomplete class type.
-7- 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
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
-8- An identifier can optionally be provided as a parameter name; if present in a function definition (dcl.fct.def), it names a parameter (sometimes called ``formal argument''). [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 the parameter-declaration-clause since it goes out of scope at the end of the function declarator (basic.scope). ]
-9- [Example: the declaration
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. ] [Note: typedefs are sometimes convenient when the return type of a function is complex. For example, the function fpif above could have been declaredint i, *pi, f(), *fpi(int), (*pif)(const char*, const char*); (*fpif(int))(int);
typedef int IFUNC(int); IFUNC* fpif(int);
-1- If an expression is specified in a parameter declaration this expression is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
-2- [Example: the declaration
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:void point(int = 3, int = 4);
The last two calls are equivalent to point(1,4) and point(3,4), respectively. ]point(1,2); point(1); point();
-3- A default argument expression shall be specified only in the parameter-declaration-clause of a function declaration or in a template-parameter (temp.param). If it is specified in a parameter-declaration-clause, it shall not occur within a declarator or abstract-declarator of a parameter-declaration.*
[Footnote: This means that default arguments cannot appear, for example, in declarations of pointers to functions, references to functions, or typedef declarations. --- end foonote]
-4- 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, all parameters subsequent to a parameter with a default argument shall have default arguments supplied in this or previous declarations. A default argument shall not be redefined by a later declaration (not even to the same value). [Example:
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) }
-5- A default argument expression is implicitly converted (clause conv) to the parameter type. The default argument expression has the same semantic constraints as the initializer expression in a declaration of a variable of the parameter type, using the copy-initialization semantics (dcl.init). The names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression 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(1):
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)) } }
-6- 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. [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
-7- Local variables shall not be used in default argument expressions. [Example:
void f() { int i; extern void g(int x = i); //error // ... }
-8- The keyword this shall not be used in a default argument of a member function. [Example:
class A { void f(A* p = this) { } //error };
-9- Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in default argument expressions, even if they are not evaluated. Parameters of a function declared before a default argument expression 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)); // error, parameter a used // in default argument
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. ] A default argument is not part of the type of a function. [Example:int b; class X { int a; int mem1(int i = a); //error: nonstatic member a // used as default argument int mem2(int i = b); // OK; use X::b static int b; };
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
-10- 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() }
-1- Function definitions have the form
The declarator in a function-definition shall have the formfunction-definition: decl-specifier-seqopt declarator ctor-initializeropt function-body decl-specifier-seqopt declarator function-try-block function-body: compound-statement
as described in dcl.fct. A function shall be defined only in namespace or class scope.D1 (parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt
-2- [Example: a simple example of a complete function definition is
Here int is the decl-specifier-seq; max(int a, int b, int c) is the declarator; { /* ... */ } is the function-body. ]int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; }
-3- A ctor-initializer is used only in a constructor; see class.ctor and class.init.
-4- A cv-qualifier-seq can be part of a non-static member function declaration, non-static member function definition, or pointer to member function only; see class.this. It is part of the function type.
-5- [Note: unused parameters need not be named. For example,
void print(int a, int) { printf("a = %d\n",a); }
-1- A declarator can specify an initial value for the identifier being declared. The identifier designates an object or reference being initialized. The process of initialization described in the remainder of dcl.init applies also to initializations specified by other syntactic contexts, such as the initialization of function parameters with argument expressions (expr.call) or the initialization of return values (stmt.return).
initializer: = initializer-clause ( expression-list )
initializer-clause: assignment-expression { initializer-list ,opt } { }
initializer-list: initializer-clause initializer-list , initializer-clause
-2- Automatic, register, static, and external variables of namespace scope can be initialized by arbitrary expressions involving literals and previously declared variables and functions. [Example:
int f(int); int a = 2; int b = f(a); int c(b);
-3- [Note: default argument expressions are more restricted; see dcl.fct.default.
-4- The order of initialization of static objects is described in basic.start and stmt.dcl. ]
-5- To zero-initialize storage for an object of type T means:
[Footnote: This member must not be static, by virtue of the requirements in class.union. --- end foonote]is zero-initialized;
-6- The memory occupied by any object of static storage duration shall be zero-initialized at program startup before any other initialization takes place. [Note: in some cases, additional initialization is done later. ]
-7- An object whose initializer is an empty set of parentheses, i.e., (), shall be default-initialized.
-8- [Note: since () is not permitted by the syntax for initializer,
is not the declaration of an object of class X, but the declaration of a function taking no argument and returning an X. The form () is permitted in certain other initialization contexts (expr.new, expr.type.conv, class.base.init). ]X a();
-9- If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value*;
[Footnote: This does not apply to aggregate objects with automatic storage duration initialized with an incomplete brace-enclosed initializer-list; see dcl.init.aggr. --- end foonote]if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
-10- An initializer for a static member is in the scope of the member's class. [Example:
int a; struct X { static int a; static int b; }; int X::a = 1; int X::b = a; //X::b = X::a
-11- The form of initialization (using parentheses or =) is generally insignificant, but does matter when the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.
-12- The initialization that occurs in argument passing, function return, throwing an exception (except.throw), handling an exception (except.handle), and brace-enclosed initializer lists (dcl.init.aggr) is called copy-initialization and is equivalent to the form
The initialization that occurs in new expressions (expr.new), static_cast expressions (expr.static.cast), functional notation type conversions (expr.type.conv), and base and member initializers (class.base.init) is called direct-initialization and is equivalent to the formT x = a;
T x(a);
-13- If T is a scalar type, then a declaration of the form
is equivalent toT x = { a };
T x = a;
-14- The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. The source type is not defined when the initializer is brace-enclosed or when it is a parenthesized list of expressions.
int a; const int b = a; int c = b;
-1- An aggregate is an array or a class (clause class) with no user-declared constructors (class.ctor), no private or protected non-static data members (clause class.access), no base classes (clause class.derived), and no virtual functions (class.virtual).
-2- When an aggregate is initialized the initializer can be an initializer-clause consisting of a brace-enclosed, comma-separated list of initializers for the members of the aggregate, written in increasing subscript or member order. If the aggregate contains subaggregates, this rule applies recursively to the members of the subaggregate. [Example:
initializes a.x with 1, a.b.i with 2, a.b.j with 3. ]struct A { int x; struct B { int i; int j; } b; } a = { 1, { 2, 3 } };
-3- An aggregate that is a class can also be initialized with a single expression not enclosed in braces, as described in dcl.init.
-4- An array of unknown size initialized with a brace-enclosed initializer-list containing n initializers, where n shall be greater than zero, is defined as having n elements (dcl.array). [Example:
declares and initializes x as a one-dimensional array that has three elements since no size was specified and there are three initializers. ] An empty initializer list {} shall not be used as the initializer for an array of unknown bound.*int x[] = { 1, 3, 5 };
[Footnote: The syntax provides for empty initializer-lists, but nonetheless C++ does not have zero length arrays. --- end foonote]
-5- Static data members are not considered members of the class for purposes of aggregate initialization. [Example:
Here, the second initializer 2 initializes a.j and not the static data member A::s. ]struct A { int i; static int s; int j; } a = { 1, 2 };
-6- An initializer-list is ill-formed if the number of initializers exceeds the number of members or elements to initialize. [Example:
is ill-formed. ]char cv[4] = { 'a', 's', 'd', 'f', 0 }; //error
-7- If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized (dcl.init). [Example:
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0. ]struct S { int a; char* b; int c; }; S ss = { 1, "asdf" };
-8- An initializer for an aggregate member that is an empty class shall have the form of an empty initializer-list {}. [Example:
struct S { }; struct A { S s; int i; } a = { { } , 3 };
-9- If an incomplete or empty initializer-list leaves a member of reference type uninitialized, the program is ill-formed.
-10- When initializing a multi-dimensional array, the initializers initialize the elements with the last (rightmost) index of the array varying the fastest (dcl.array). [Example:
initializes x[0][0] to 3, x[0][1] to 1, x[1][0] to 4, and x[1][1] to 2. On the other hand,int x[2][2] = { 3, 1, 4, 2 };
initializes the first column of y (regarded as a two-dimensional array) and leaves the rest zero. ]float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } };
-11- Braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializers initializes the members of a subaggregate; it is erroneous for there to be more initializers than members. If, however, the initializer-list for a subaggregate does not begin with a left brace, then only enough initializers from the list are taken to initialize the members of the subaggregate; any remaining initializers are left to initialize the next member of the aggregate of which the current subaggregate is a member. [Example:
is a completely-braced initialization: 1, 3, and 5 initialize the first row of the array y[0], namely y[0][0], y[0][1], and y[0][2]. Likewise the next two lines initialize y[1] and y[2]. The initializer ends early and therefore y[3]'s elements are initialized as if explicitly initialized with an expression of the form float(), that is, are initialized with 0.0. In the following example, braces in the initializer-list are elided; however the initializer-list has the same effect as the completely-braced initializer-list of the above example,float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
The initializer for y begins with a left brace, but the one for y[0] does not, therefore three elements from the list are used. Likewise the next three are taken successively for y[1] and y[2].float y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
-12-
All implicit type conversions (clause conv) are considered when
initializing the aggregate member with an initializer from an
initializer-list.
If the
initializer
can initialize a member, the member is initialized.
Otherwise, if the member is itself a non-empty subaggregate,
brace elision is assumed and the
initializer
is considered for the initialization of the first member of the subaggregate.
[Example:
Braces are elided around the initializer for b.a1.i. b.a1.i is initialized with 4, b.a2 is initialized with a, b.z is initialized with whatever a.operator int() returns. ]struct A { int i; operator int(); }; struct B { A a1, a2; int z; }; A a; B b = { 4, a, a };
-13- [Note: An aggregate array or an aggregate class may contain members of a class type with a user-declared constructor (class.ctor). Initialization of these aggregate objects is described in class.expl.init. ]
-14- When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a POD type, the initialization shall be done during the static phase of initialization (basic.start.init); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization.
-15- When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer for the first member of the union. [Example:
union u { int a; char* b; }; u a = { 1 }; u b = a; u c = 1; //error u d = { 0, "asdf" }; // error u e = { "asdf" }; // error
-1- A char array (whether plain char, signed char, or unsigned char) can be initialized by a string-literal (optionally enclosed in braces); a wchar_t array can be initialized by a wide string-literal (optionally enclosed in braces); successive characters of the string-literal initialize the members of the array. [Example:
shows a character array whose members are initialized with a string-literal. Note that because '\n' is a single character and because a trailing '\0' is appended, sizeof(msg) is 25. ]char msg[] = "Syntax error on line %s\n";
-2- There shall not be more initializers than there are array elements. [Example:
is ill-formed since there is no space for the implied trailing '\0'. ]char cv[4] = "asdf"; //error
-1- A variable declared to be a T&, that is ``reference to type T'' (dcl.ref), shall be initialized by an object, or function, of type T or by an object that can be converted into a T. [Example:
int g(int); void f() { int i; int& r = i; //r refers to i r = 1; // the value of i becomes 1 int* p = &r; // p points to i int& rr = r; // rr refers to what r refers to, that is, to i int (&rg)(int) = g; // rg refers to the function g rg(i); // calls function g int a[3]; int (&ra)[3] = a; // ra refers to the array a ra[1] = i; // modifies a[1] }
-2- A reference cannot be changed to refer to another object after initialization. Note that initialization of a reference is treated very differently from assignment to it. Argument passing (expr.call) and function value return (stmt.return) are initializations.
-3- The initializer can be omitted for a reference only in a parameter declaration (dcl.fct), in the declaration of a function return type, in the declaration of a class member within its class declaration (class.mem), and where the extern specifier is explicitly used. [Example:
int& r1; //error: initializer missing extern int& r2; // OK
-4- Given types ``cv1 T1'' and ``cv2 T2,'' ``cv1 T1'' is reference-related to ``cv2 T2'' if T1 is the same type as T2, or T1 is a base class of T2. ``cv1 T1'' is reference-compatible with ``cv2 T2'' if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. For purposes of overload resolution, cases for which cv1 is greater cv-qualification than cv2 are identified as reference-compatible with added qualification (see over.ics.rank). In all cases where the reference-related or reference-compatible relationship of two types is used to establish the validity of a reference binding, and T1 is a base class of T2, a program that necessitates such a binding is ill-formed if T1 is an inaccessible (clause class.access) or ambiguous (class.member.lookup) base class of T2.
-5- A reference to type ``cv1 T1'' is initialized by an expression of type ``cv2 T2'' as follows:
[Footnote: This requires a conversion function (class.conv.fct) returning a reference type. --- end foonote](this conversion is selected by enumerating the applicable conversion functions (over.match.ref) and choosing the best one through overload resolution (over.match)),
double d = 2.0; double& rd = d; //rd refers to d const double& rcd = d; // rcd refers to d struct A { }; struct B : public A { } b; A& ra = b; // ra refers to A sub-object in b const A& rca = b; // rca refers to A sub-object in b
double& rd2 = 2.0; //error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const
[Footnote: Clearly, if the reference initialization being processed is one for the first argument of a copy constructor call, an implementation must eventually choose the first alternative (binding without copying) to avoid infinite recursion. --- end foonote]
struct A { }; struct B : public A { } b; extern B f(); const A& rca = f(); //Either bound to the A sub-object of the B rvalue, // or the entire B object is copied and the reference // is bound to the A sub-object of the copy
const double& rcd2 = 2; //rcd2 refers to temporary with value 2.0 const volatile int cvi = 1; const int& r = cvi; // error: type qualifiers dropped
-6- [Note: class.temporary describes the lifetime of temporaries bound to references. ]