______________________________________________________________________ 8 Declarators [dcl.decl] ______________________________________________________________________ 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 fundamental type, storage class, or other properties of the objects and functions being declared. The declara tors specify the names of these objects and functions and (optionally) modify the type 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.1) 4 Declarators have the syntax declarator: direct-declarator ptr-operator declarator _________________________ 1) A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single declarator. That is T D1, D2, ... Dn; is usually equvalent to T D1; T D2; ... T Dn; where T is a decl-specifier-seq and each Di is a init-declarator. The exception occurs when one declarator modifies the name environment used by a following declarator, as in struct S { ... }; S S, T; // declare two instances of struct S which is not equivalent to struct S { ... }; S S; S T; // error 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 declarator-id: id-expression nested-name-specifieropt type-name 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 opera tor :: (_expr.prim_, _class.ctor_, _class.dtor_). 8.1 Type names [dcl.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 direct-abstract-declarator: direct-abstract-declaratoropt ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt direct-abstract-declaratoropt [ constant-expressionopt ] ( 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: int // int i int * // int *pi int *[3] // int *p[3] int (*)[3] // int (*p3i)[3] int *() // int *f() int (*)(double) // int (*pf)(double) name respectively the types "integer," "pointer to integer," "array of 3 pointers to integers," "pointer to array of 3 integers," "function having no parameters and returning pointer to integer," and "pointer to function of double returning an integer." ] 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, it surfaces as a choice 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 statements, the resolution is to consider any construct that could possibly be a declaration a declaration. A declaration can be explicitly disambiguated by a non function-style cast or a = to indicate initialization. [Example: struct S { S(int); }; void foo(double a) { S x(int(a)); // function declaration S x(int()); // function declaration S y((int)a); // object declaration S z = int(a); // object declaration } --end example] 2 The ambiguity arising from the similarity between a function-style cast and a type-id can occur in many different contexts. The ambigu ity surfaces 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 consid ered a type-id. 3 [Example: #include <cstddef> char *p; void *operator new(size_t, int); void foo(int x) { 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) } --end example] 8.3 Meaning of declarators [dcl.meaning] 1 A list of declarators appears after an optional (_dcl.dcl_) decl- specifier-seq (_dcl.spec_). Each declarator contains exactly one declarator-id; it names the identifier that is declared. A declara tor-id shall be a simple identifier, except for the following cases: the declaration of some special functions (_class.conv_, _class.dtor_, _over.oper_), the definition of a member function (_class.mfct_), the definition of a static data member (_class.static_), the declaration of a friend function that is a member of another class (_class.friend_). An auto, static, extern, register, 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. 2 Thus, a declaration of a particular identifier has the form T D where T is a decl-specifier-seq and D is a declarator. The following subsections give an inductive procedure for determining the type spec ified for the contained declarator-id by such a declaration. 3 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 dec laration int unsigned i; the type specifiers int unsigned determine the type unsigned int" (_dcl.type.simple_). ] 4 In a declaration T D where D is an unadorned identifier the type of this identifier is T." 5 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] 1 In a declaration T D where D has the form * 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. 2 [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; // okay, but would make p point to ci ... // ... because of previous error *p = 5; // clobber ci --end example] 3 volatile specifiers are handled similarly. 4 See also _expr.ass_ and _dcl.init_. 5 There can be no pointers to references (_dcl.ref_) or pointers to bit- fields (_class.bit_). 8.3.2 References [dcl.ref] 1 In a declaration T D where D has the form & 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." At all times during the determination of a type, any type of the form "cv-qualifier-seq refer ence to T" is adjusted to be "reference to T". [Example: in typedef int& A; const A aref = 3; the type of aref is "reference to int", not "const reference to int". ] A declarator that specifies the type "reference to cv void" is ill- formed. 2 [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_. ] 3 It is unspecified whether or not a reference requires storage (_basic.stc_). 4 There shall be no references to references, no references to bit- fields (_class.bit_), no arrays of references, and no pointers to ref erences. 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_) declara tion 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. In particular, null references are prohibited; no diagnostic is required. 8.3.3 Pointers to members [dcl.mptr] 1 In a declaration T D where D has the form ::opt nested-name-specifier * cv-qualifier-seqopt D1 and the nested-name-specifier names a class, and the type of the iden tifier 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." 2 [Example: 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; 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] 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: There is no "reference-to-member" type in C++. See also _expr.mptr.oper_ and _expr.unary_. ] 8.3.4 Arrays [dcl.array] 1 In a declaration T D where D has the form D1 [constant-expressionopt] 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 shall not be a reference type, an incomplete type, a function type or an abstract class type. If the constant-expression (_expr.const_) is present, 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." If the constant expres sion 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_. At all times during the determination of a type, 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: typedef int A[5], AA[2][3]; const A x; // type is ``array of 5 const int'' const AA y; // type is ``array of 2 array of 3 const int'' --end example] 2 An array can be constructed from one of the fundamental types2) (except void), from a pointer, from a pointer to member, from a class, or from another array. 3 When several "array of" specifications are adjacent, a multidimen sional 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: 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. ] 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 sub script operator [] is interpreted in such a way that E1[E2] is identi cal 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, _________________________ 2) The enumeration types are included in the fundamental types. 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 expres sion 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 con verted into a pointer. 8 [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. ] 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 D1 ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt 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 cv-qualifier-seqopt function with parameters of type parameter-declaration-clause and returning T"; a type of this form is a function type3). 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 _________________________ 3) As indicated by the syntax, cv-qualifiers are a significant compo nent in function return types. 2 The parameter-declaration-clause determines the arguments that can be specified, and their processing, when the function is called. If the parameter-declaration-clause terminates with an ellipsis, the number of arguments shall be equal to or greater than the number of parame ters specified; if it 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). Where syntacti cally correct, ", ..." is synonymous with "...". [Note: the standard header <cstdarg> contains a mechanism for accessing arguments passed using the ellipsis (see _expr.call_ and _lib.support.runtime_). ] 3 A single name can be used for several different functions in a single scope; this is function overloading (_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 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, sev eral transformations take place upon the types. Any cv-qualifier mod ifying a parameter type is deleted; e.g., the type void(const int) becomes void(int). Such cv-qualifiers affect only the definition of the parameter within the body of the function. If the storage-class- specifier register modifies a parameter type, the specifier is deleted; e.g., register char* becomes char*. Such storage-class- qualifiers affect only the definition of the parameter within the body of the function. The resulting list of transformed parameter types is the function's parameter type list. The return type and the parameter type list, but not the default arguments (_dcl.fct.default_) or excep tion specification (_except.spec_), are part of the function type. 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.4) A cv-qualifier-seq can only be part of a declaration or definition of a nonstatic member function, and of a pointer to a member function; see _class.this_. It is part of the function type. 4 Functions shall not return arrays or functions, although they can return pointers and references to such things. There shall be no arrays of functions, although there can be arrays of pointers to func tions. _________________________ 4) 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 pa rameter is a pointer to function or pointer to member function then to its parameters also, etc. 5 Types shall not be defined in return or parameter types. 6 [Note: the parameter-declaration-clause is used to check and convert arguments in calls and to check pointer-to-function, reference-to- function, and pointer-to-member-function assignments and initializa tions. ] 7 An identifier can optionally be provided as a parameter name; if pre sent 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 an identifier is present in a function dec laration, it cannot be used since it goes out of scope at the end of the function declarator (_basic.scope_); ] 8 [Note: The exception-specification is described in _except.spec_ . ] 9 [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 inte ger 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. 10Typedefs are sometimes convenient when the return type of a function is complex. For another example, the function fpif above could have been declared typedef int IFUNC(int); IFUNC* fpif(int); 11The declaration int fseek(FILE*, long, int); declares a function taking three arguments of the specified types, and returning int (_dcl.type_). 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*. 12 --end example] 8.3.6 Default arguments [dcl.fct.default] 1 If an expression is specified in a parameter declaration this expres sion is used as a default argument. Default arguments will be used in calls where trailing arguments are missing. 2 [Example: the declaration void point(int = 3, int = 4); declares a function that can be called with zero, one, or two argu ments 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. ] 3 A default argument expression shall be specified only in the parame ter-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.5) 4 For non-template functions, default arguments can be added in later declarations of a function in the same scope. Declarations in differ ent 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 } _________________________ 5) This means that default arguments cannot appear, for example, in declarations of pointers to functions, references to functions, or typedef declarations. 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] Declarations of a given nonmember function in differ ent translation units need not specify the same default arguments. Declarations of a given member function in different translation units, however, shall specify the same default arguments (the accumu lated sets of default arguments at the end of the translation units shall be the same). 5 Default argument expressions have their names bound and their types checked at the point of declaration. [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)) } } --end example] 6 In member function declarations, names in default argument expressions are looked up in the scope of the class like names in member function bodies (_class.scope0_). The default arguments in an out-of-line function 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 --end example] 7 Local variables shall not be used in default argument expressions. [Example: void f() { int i; extern void g(int x = i); // error // ... } --end example] 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 }; --end example] 9 Default arguments are evaluated at each point of call before entry into a function. The order of evaluation of function arguments is implementation-defined. 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 argu ment 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 --end example] Similarly, a nonstatic member shall not be used in a default argument expression, even if it is not evaluated, 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 fol lowing example is ill-formed because no object is supplied for the nonstatic member X::a used as an initializer. 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 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 _class_. ] 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(); // fine, 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 imported as well. 10A 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->A::f(7) pb->f(); // error: wrong number of arguments for B::f() } --end example] 8.4 Function definitions [dcl.fct.def] 1 Function definitions have the form function-definition: decl-specifier-seqopt declarator ctor-initializeropt function-body decl-specifier-seqopt declarator function-try-block function-body: compound-statement The declarator in a function-definition shall have the form D1 ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt as described in _dcl.fct_. A function shall be defined only in names pace or class scope. 2 The parameters are in the scope of the outermost block of the func tion-body. 3 [Example: a simple example of a complete function definition is int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; } Here int is the decl-specifier-seq; max(int a, int b, int c) is the declarator; { /* ... */ } is the function-body. ] 4 A ctor-initializer is used only in a constructor; see _class.ctor_ and _class.init_. 5 A cv-qualifier-seq can be part of a non-static member function decla ration, non-static member function definition, or pointer to member function only; see _class.this_. It is part of the function type. 6 [Note: unused parameters need not be named. For example, void print(int a, int) { printf("a = %d\n",a); } --end note] 8.5 Initializers [dcl.init] 1 A declarator can specify an initial value for the identifier being declared. The identifier designates an object or reference being ini tialized. The process of initialization described in the remainder of this subclause (_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 initializa tion 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 constants and previously declared variables and functions. [Example: int f(int); int a = 2; int b = f(a); int c(b); --end example] 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: --if T is a scalar or pointer-to-member type, the storage is set to the value of 0 (zero) converted to T; --if T is a non-union class type, the storage for each nonstatic data member and each base-class subobject is zero-initialized; --if T is a union type, the storage for its first nonstatic data mem ber is zero-initialized; --if T is an array type, the storage for each element is zero- initialized; --if T is a reference type, no initialization is performed. To default-initialize an object of type T means: --if T is a non-POD class type, the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor); --if T is an array type, each element is default-initialized; --otherwise, the storage for the object is zero-initialized. Default-initialization uses the direct-initialization semantics described below. 6 The memory occupied by any object of static storage duration shall be zero-initialized. Furthermore, if no initializer is explicitly speci fied in the declaration of the object and the object is of non-POD class type (or array thereof), then default initialization shall be performed. If no initializer is specified for an object with auto matic or dynamic storage duration, the object and its subobjects, if any, have an indeterminate initial value.6) 7 An initializer for a static member is in the scope of the member's class. [Example: _________________________ 6) This does not apply to aggregate objects with automatic storage du ration initialized with an incomplete brace-enclosed initializer-list; see _dcl.init.aggr_. int a; struct X { static int a; static int b; }; int X::a = 1; int X::b = a; // X::b = X::a --end example] 8 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. 9 [Note: since () is not permitted by the syntax for initializer, X a(); 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 per mitted in certain other initialization contexts (_expr.new_, _expr.type.conv_, _class.base.init_). ] 10The initialization that occurs in argument passing, function return, and brace-enclosed initializer lists (_dcl.init.aggr_) is called copy- initialization and is equivalent to the form T x = a; 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 form T x(a); 11The 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. --If the destination type is a reference type, see _dcl.init.ref_. --If the destination type is an array of characters or an array of wchar_t, and the initializer is a string literal, see _dcl.init.string_. --Otherwise, if the destination type is an array, see _dcl.init.aggr_. --If the destination type is a (possibly cv-qualified) class type: --If the class is an aggregate (_dcl.init.aggr_), and the initial izer is a brace-enclosed list, see _dcl.init.aggr_. --If the initialization is direct-initialization, or if it is copy- initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the des tination, constructors are considered. The applicable construc tors are enumerated (_over.match.ctor_), and the best one is cho sen through overload resolution (_over.match_). The constructor so selected is called to initialize the object, with the initial izer expression(s) as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed. --Otherwise (i.e., for the remaining copy-initialization cases), a temporary of the destination type is created. User-defined con versions that can convert from the source type to the destination type are enumerated (_over.match.user_), and the best one is cho sen through overload resolution (_over.match_). The user-defined conversion so selected is called to convert the initializer expression into the temporary. If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The object being initialized is then direct-initialized from the temporary according to the rules above.7) In certain cases, an implementa tion is permitted to eliminate the temporary by initializing the object directly; see _class.temporary_. --Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conver sion functions are enumerated (_over.match.user_), and the best one is chosen through overload resolution (_over.match_). The user- defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed. --Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. Standard conversions (clause _conv_) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the des tination type; no user-defined conversions are considered. If the conversion cannot be done, the initialization is ill-formed. [Note: an expression of type "cv1 T" can initialize an object of type "cv2 T" independently of the cv-qualifiers cv1 and cv2. int a; const int b = a; int c = b; --end note] 12If T is a scalar type, then a declaration of the form T x = { a }; is equivalent to T x = a; _________________________ 7) Because the type of the temporary is the same as the type of the object being initialized, this direct-initialization, if well-formed, will use a copy constructor (_class.copy_) to copy the temporary. 8.5.1 Aggregates [dcl.init.aggr] 1 An aggregate is an array or a class (_class_) with no user-declared constructors (_class.ctor_), no private or protected non-static data members (_class.access_), no non-static members of reference type, no non-static const members, no base classes (_class.derived_), and no virtual functions (_class.virtual_).8) 2 When an aggregate is initialized the initializer can be an initial izer-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: struct A { int x; struct B { int i; int j; } b; } a = { 1, { 2, 3 } }; initializes a.x with 1, a.b.i with 2, a.b.j with 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 initial izer-list containing n initializers, where n shall be greater than zero, is defined as having n elements (_dcl.array_). [Example: int x[] = { 1, 3, 5 }; 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.8) 5 Static data members are not considered members of the class for pur poses of aggregate initialization. [Example: struct A { int i; static int s; int j; } a = { 1, 2 }; Here, the second initializer 2 initializes a.j and not the static data member A::s. ] 6 An initializer-list is ill-formed if the number of initializers exceeds the number of members or elements to initialize. [Example: char cv[4] = { 'a', 's', 'd', 'f', 0 }; // error is ill-formed. ] _________________________ 8) The syntax provides for empty initializer-lists, but nonetheless C++ does not have zero length arrays. 7 If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized with a value of the form T() (_expr.type.conv_), where T represents the type of the uninitialized member. [Example: struct S { int a; char* b; int c; }; S ss = { 1, "asdf" }; 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. ] 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 }; --end example] An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each mem ber of the aggregate shall be initialized with a value of the form T() (_expr.type.conv_), where T represents the type of the uninitialized member. 9 When initializing a multi-dimensional array, the initializers initial ize the elements with the last (rightmost) index of the array varying the fastest (_dcl.array_). [Example: float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; initializes the first column of y (regarded as a two-dimensional array) and leaves the rest zero. ] 10Braces can be elided in an initializer-list as follows. If the ini tializer-list begins with a left brace, then the succeeding comma- separated list of initializers initializes the members of a subaggre gate; 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: float y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 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 explic itly initialized with an expression of the form float(), that is, are initialized with 0.0. In the following example, braces in the ini tializer-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]. --end exam ple] 11All type conversions (_over.match.user_) are considered when initial izing the aggregate member with an initializer from an initializer- list. If the initializer can initialize a member, the member is ini tialized. Otherwise, if the member is itself a non-empty subaggre gate, brace elision is assumed and the initializer is considered for the initialization of the first member of the subaggregate. [Example: struct A { int i; operator int(); }; struct B { A a1, a2; int z; }; A a; B b = { 4, a, a }; Braces are elided around the initializer for b.a1.i. b.a1.i is ini tialized with 4, b.a2 is initialized with a, b.z is initialized with whatever a.operator int() returns. ] 12[Note: An aggregate array or an aggregate class may contain members of a class type with a user-declared constructor (_class.ctor_). Ini tialization of these aggregate objects is described in _class.expl.init_. ] 13When an aggregate is initialized with a brace-enclosed initializer- list, if some members are initialized with constant expressions and other members are initialized with non-constant expressions, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization (_basic.start.init_). 14The initializer for a union with no user-declared constructor is either a single expression of the same type, or a brace-enclosed ini tializer 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 --end example] 8.5.2 Character arrays [dcl.init.string] 1 A char array (whether plain char, signed, or unsigned) can be initial ized by a string; a wchar_t array can be initialized by a wide string literal; successive characters of the string initialize the members of the array. [Example: char msg[] = "Syntax error on line %s\n"; shows a character array whose members are initialized with a string. Note that because '\n' is a single character and because a trailing '\0' is appended, sizeof(msg) is 25. ] 2 There shall not be more initializers than there are array elements. [Example: char cv[4] = "asdf"; // error is ill-formed since there is no space for the implied trailing '\0'. ] 8.5.3 References [dcl.init.ref] 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]' } --end example] 2 A reference cannot be changed to refer to another object after ini tialization. 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 --end example] 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 rela tionship 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 (_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: --If the initializer expression is an lvalue (but not an lvalue for a bit-field), and 6 --"cv1 T1" is reference-compatible with "cv2 T2," or --the initializer expression can be implicitly converted to an lvalue of type "cv3 T1," where cv3 is the same cv-qualification as, or lesser cv-qualification than, cv1, 9) then 7 the reference is bound directly to the initializer expression lvalue. [Note: the usual lvalue-to-rvalue (_conv.lval_), array-to- pointer (_conv.array_), and function-to-pointer (_conv.func_) stan dard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done. ] [Example: 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' --end example] 8 --Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [Example: 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 --end example] _________________________ 9) This requires a conversion function (_class.conv.fct_) returning a reference type, and therefore applies only when T2 is a class type. --If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2," the reference is bound in one of the following ways (the choice is implementa tion-defined): --The reference is bound directly to the object represented by the rvalue (see _basic.lval_) or to a sub-object within that object. --A temporary of type "cv1 T2" [sic] is created, and a copy con structor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub- object within the temporary.10) 9 The appropriate copy constructor must be callable whether or not the copy is actually done. [Example: struct A { }; struct B : public A { } b; extern B f(); const A& rca = f(); // Either bound directly or // the entire B object is copied and // the reference is bound to the // A sub-object of the copy --end example] 10 --Otherwise, a temporary of type "cv1 T1" is created and initialized from the initializer expression using the rules for a non- reference initialization (_dcl.init_). The reference is then bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed. [Example: 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 --end example] 11 [Note: _class.temporary_ describes the lifetime of temporaries bound to references. ] _________________________ 10) Clearly, if the reference initialization being processed is one for the first argument of a copy constructor call, an implementation must eventually choose the direct-binding alternative to avoid infi nite recursion.