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 a ctor-initializer, which has the form
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, a mem-initializer-id naming the member or base class and composed of a single identifier refers to the class member.
A mem-initializer-id for the hidden base class can be specified using a qualified name.
— end note]
Unless the mem-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, the mem-initializer is ill-formed.
A mem-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 a mem-initializer-id is ambiguous because it designates both a direct non-virtual base class and an indirect virtual base class, the mem-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]
A ctor-initializer may initialize a variant member of the constructor's class.
If a ctor-initializer specifies more than one mem-initializer for the same member or for the same base class, the ctor-initializer is ill-formed.
A mem-initializer-list can delegate to another constructor of the constructor's class using any class-or-decltype that denotes the constructor's class itself.
If a mem-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 the mem-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]
The expression-list or braced-init-list in a mem-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 a mem-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) { } // error const 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 a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
  • if the entity is a non-static data member that has a default member initializer ([class.mem]) and either 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 class X 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 of X is neither initialized nor given a value during execution of the compound-statement of the body of the constructor, the member has an indeterminate value.
— 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 constructor int i; // OK: i has indeterminate value int 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 the mem-initializer is performed, and the non-static data member's default member initializer is ignored.
[Example 7:
Given struct 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 of arg, and the side 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) { } // OK const 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 class base-specifier-list.
  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-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) { } };
initializes X​::​r to refer to X​::​a, initializes X​::​b with the value of the constructor parameter i, initializes X​::​i with the value of the constructor parameter i, and initializes X​::​j with the value of X​::​i; this takes place each time an object of class X is created.
— end example]
Member functions (including virtual member functions, [class.virtual]) can be called for an object under construction.
Similarly, an object under construction can be the operand of the typeid operator ([expr.typeid]) or of a dynamic_­cast ([expr.dynamic.cast]).
However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, 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 result of virtual function calls, typeid and dynamic_­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]