20 KiB
[class.base.init]
11 Classes [class]
11.9 Initialization [class.init]
11.9.3 Initializing bases and members [class.base.init]
In the definition of a constructor for a class, initializers for direct and virtual base class subobjects and non-static data members can be specified by actor-initializer, which has the form
mem-initializer-list:
mem-initializer ...opt
mem-initializer-list , mem-initializer ...opt
mem-initializer:
mem-initializer-id ( expression-listopt )
mem-initializer-id braced-init-list
mem-initializer-id:
class-or-decltype
identifier
Lookup for an unqualified name in a mem-initializer-id ignores the constructor's function parameter scope.
[Note 1:
If the constructor's class contains a member with the same name as a direct or virtual base class of the class, amem-initializer-id naming the member or base class and composed of a single identifier refers to the class member.
Amem-initializer-id for the hidden base class can be specified using a qualified name.
â end note]
Unless themem-initializer-id names the constructor's class, a non-static data member of the constructor's class, or a direct or virtual base of that class, themem-initializer is ill-formed.
Amem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.
[Example 1: struct A { A(); };typedef A global_A;struct B { };struct C: public A, public B { C(); }; C::C(): global_A() { } // mem-initializer for base A â end example]
If amem-initializer-id is ambiguous because it designates both a direct non-virtual base class and an indirect virtual base class, themem-initializer is ill-formed.
[Example 2: struct A { A(); };struct B: public virtual A { };struct C: public A, public B { C(); }; C::C(): A() { } // error: which A? â end example]
Actor-initializer may initialize a variant member of the constructor's class.
If actor-initializer specifies more than onemem-initializer for the same member or for the same base class, thector-initializer is ill-formed.
A mem-initializer-list can delegate to another constructor of the constructor's class using anyclass-or-decltype that denotes the constructor's class itself.
If amem-initializer-id designates the constructor's class, it shall be the only mem-initializer; the constructor is a delegating constructor, and the constructor selected by themem-initializer is the target constructor.
The target constructor is selected by overload resolution.
Once the target constructor returns, the body of the delegating constructor is executed.
If a constructor delegates to itself directly or indirectly, the program is ill-formed, no diagnostic required.
[Example 3: struct C { C( int ) { } // #1: non-delegating constructor C(): C(42) { } // #2: delegates to #1 C( char c ) : C(42.0) { } // #3: ill-formed due to recursion with #4 C( double d ) : C('a') { } // #4: ill-formed due to recursion with #3}; â end example]
Theexpression-list or braced-init-list in amem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of [dcl.init] for direct-initialization.
[Example 4: struct B1 { B1(int); /* ... / };struct B2 { B2(int); / ... */ };struct D : B1, B2 { D(int); B1 b; const int c;};
D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ } D d(10); â end example]
[Note 2:
The initialization performed by each mem-initializer constitutes a full-expression ([intro.execution]).
Any expression in amem-initializer is evaluated as part of the full-expression that performs the initialization.
â end note]
A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
A temporary expression bound to a reference member in a mem-initializer is ill-formed.
[Example 5: struct A { A() : v(42) { } // errorconst int& v;}; â end example]
In a non-delegating constructor other than an implicitly-defined copy/move constructor ([class.copy.ctor]), if a given potentially constructed subobject is not designated by amem-initializer-id (including the case where there is nomem-initializer-list because the constructor has noctor-initializer), then
if the entity is a non-static data member that has a default member initializer ([class.mem]) and either
the constructor's class is a union ([class.union]), and no other variant member of that union is designated by a mem-initializer-id or
the constructor's class is not a union, and, if the entity is a member of an anonymous union, no other member of that union is designated by amem-initializer-id,
the entity is initialized from its default member initializer as specified in [dcl.init];
otherwise, if the entity is an anonymous union or a variant member ([class.union.anon]), no initialization is performed;
otherwise, the entity is default-initialized ([dcl.init]).
[Note 3:
An abstract class ([class.abstract]) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers can be omitted.
â end note]
An attempt to initialize more than one non-static data member of a union renders the program ill-formed.
[Note 4:
After the call to a constructor for classX for an object with automatic or dynamic storage duration has completed, if the constructor was not invoked as part of value-initialization and a member ofX is neither initialized nor given a value during execution of the compound-statement of the body of the constructor, the member has an indeterminate or erroneous value ([basic.indet]).
â end note]
[Example 6: struct A { A();};
struct B { B(int);};
struct C { C() { } // initializes members as follows: A a; // OK, calls A::A()const B b; // error: B has no default constructorint i; // OK, i has indeterminate or erroneous valueint j = 5; // OK, j has the value 5}; â end example]
If a given non-static data member has both a default member initializer and a mem-initializer, the initialization specified by themem-initializer is performed, and the non-static data member's default member initializer is ignored.
[Example 7:
Givenstruct A {int i = /* some integer expression with side effects */ ; A(int arg) : i(arg) { }// ...}; the A(int) constructor will simply initialize i to the value ofarg, and theside effects in i's default member initializer will not take place.
â end example]
A temporary expression bound to a reference member from a default member initializer is ill-formed.
[Example 8: struct A { A() = default; // OK A(int v) : v(v) { } // OKconst int& v = 42; // OK}; A a1; // error: ill-formed binding of temporary to reference A a2(1); // OK, unfortunately â end example]
In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked ([class.dtor]).
[Note 5:
This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown ([except.ctor]).
â end note]
In a non-delegating constructor, initialization proceeds in the following order:
-
First, and only for the constructor of the most derived class ([intro.object]), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where âleft-to-rightâ is the order of appearance of the base classes in the derived classbase-specifier-list.
-
Then, direct base classes are initialized in declaration order as they appear in thebase-specifier-list (regardless of the order of themem-initializers).
-
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of themem-initializers).
-
Finally, the compound-statement of the constructor body is executed.
[Note 6:
The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization.
â end note]
[Example 9: struct V { V(); V(int);};
struct A : virtual V { A(); A(int);};
struct B : virtual V { B(); B(int);};
struct C : A, B, virtual V { C(); C(int);};
A::A(int i) : V(i) { /* ... / } B::B(int i) { / ... / } C::C(int i) { / ... */ } V v(1); // use V(int) A a(2); // use V(int) B b(3); // use V() C c(4); // use V() â end example]
[Note 7:
The expression-list or braced-init-list of a mem-initializer is in the function parameter scope of the constructor and can use this to refer to the object being initialized.
â end note]
[Example 10: class X {int a; int b; int i; int j;public:const int& r; X(int i): r(a), b(i), i(i), j(this->i) { }};
initializesX::r to refer toX::a, initializesX::b with the value of the constructor parameteri, initializesX::i with the value of the constructor parameteri, and initializesX::j with the value ofX::i; this takes place each time an object of classX is created.
â end example]
Member functions (including virtual member functions, [class.virtual]) can be called for an object under construction or destruction.
Similarly, an object under construction or destruction can be the operand of thetypeid operator ([expr.typeid]) or of adynamic_cast ([expr.dynamic.cast]).
However, if these operations are performed during evaluation of
a ctor-initializer (or in a function called directly or indirectly from actor-initializer) before all themem-initializers for base classes have completed,
a precondition assertion of a constructor, or
a postcondition assertion of a destructor ([dcl.contract.func]),
the program has undefined behavior.
[Example 11: class A {public: A(int);};
class B : public A {int j;public:int f(); B() : A(f()), // undefined behavior: calls member function but base A not yet initialized j(f()) { } // well-defined: bases are all initialized};
class C {public: C(int);};
class D : public B, C {int i;public: D() : C(f()), // undefined behavior: calls member function but base C not yet initialized i(f()) { } // well-defined: bases are all initialized}; â end example]
[Note 8:
[class.cdtor] describes the results of virtual function calls,typeid anddynamic_casts during construction for the well-defined cases; that is, describes the polymorphic behavior of an object under construction.
â end note]
A mem-initializer followed by an ellipsis is a pack expansion ([temp.variadic]) that initializes the base classes specified by a pack expansion in the base-specifier-list for the class.
[Example 12: template<class... Mixins>class X : public Mixins... {public: X(const Mixins&... mixins) : Mixins(mixins)... { }}; â end example]