[class.init] (original) (raw)

11 Classes [class]

11.9 Initialization [class.init]

11.9.1 General [class.init.general]

When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the initializer has the form(), the object is initialized as specified in [dcl.init].

An object of class type (or array thereof) can be explicitly initialized; see [class.expl.init] and [class.base.init].

When an array of class objects is initialized (either explicitly or implicitly) and the elements are initialized by constructor, the constructor shall be called for each element of the array, following the subscript order; see [dcl.array].

[Note 1:

Destructors for the array elements are called in reverse order of their construction.

— _end note_]

11.9.2 Explicit initialization [class.expl.init]

An object of class type can be initialized with a parenthesizedexpression-list, where theexpression-listis construed as an argument list for a constructor that is called to initialize the object.

Either direct-initialization semantics or copy-initialization semantics apply; see [dcl.init].

[Example 1: struct complex { complex(); complex(double); complex(double,double);}; complex sqrt(complex,complex); complex a(1); complex b = a; complex c = complex(1,2); complex d = sqrt(b,c); complex e; complex f = 3; complex g = { 1, 2 }; — _end example_]

[Note 1:

Overloading of the assignment operator ([over.ass]) has no effect on initialization.

— _end note_]

An object of class type can also be initialized by abraced-init-list.

[Example 2: complex v[6] = { 1, complex(1,2), complex(), 2 };

Here,complex​::​complex(double)is called for the initialization ofv[0]andv[3],complex​::​complex(​double, double)is called for the initialization ofv[1],complex​::​complex()is called for the initialization ofv[2],v[4], andv[5].

For another example,

struct X { int i;float f; complex c;} x = { 99, 88.8, 77.7 };

Here,x.iis initialized with 99,x.fis initialized with 88.8, andcomplex​::​complex(double)is called for the initialization ofx.c.

— _end example_]

[Note 2:

Braces can be elided in theinitializer-listfor any aggregate, even if the aggregate has members of a class type with user-defined type conversions; see [dcl.init.aggr].

— _end note_]

[Note 3:

IfTis a class type with no default constructor, any declaration of an object of typeT(or array thereof) is ill-formed if noinitializeris explicitly specified (see [class.init] and [dcl.init]).

— _end note_]

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

Lookup for an unqualified name in a mem-initializer-idignores 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-idnaming the member or base class and composed of a single identifier refers to the class member.

Amem-initializer-idfor the hidden base class can be specified using a qualified name.

— _end note_]

Unless themem-initializer-idnames the constructor's class, a non-static data member of the constructor's class, or a direct or virtual base of that class, themem-initializeris ill-formed.

Amem-initializer-listcan 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() { } — _end example_]

If amem-initializer-idis ambiguous because it designates both a direct non-virtual base class and an indirect virtual base class, themem-initializeris ill-formed.

[Example 2: struct A { A(); };struct B: public virtual A { };struct C: public A, public B { C(); }; C::C(): A() { } — _end example_]

Actor-initializermay initialize a variant member of the constructor's class.

If actor-initializerspecifies more than onemem-initializerfor the same member or for the same base class, thector-initializeris 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 ) { } C(): C(42) { } C( char c ) : C(42.0) { } C( double d ) : C('a') { } }; — _end example_]

Theexpression-listor braced-init-listin amem-initializeris 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-initializerconstitutes a full-expression ([intro.execution]).

Any expression in amem-initializeris 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-initializeris ill-formed.

[Example 5: struct A { A() : v(42) { } 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 amem-initializer-id(including the case where there is nomem-initializer-listbecause the constructor has noctor-initializer), then

[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 classXfor an object with automatic or dynamic storage duration has completed, if the constructor was not invoked as part of value-initialization and a member ofXis 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() { } A a; const B b; int i; int j = 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 = ; 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; A(int v) : v(v) { } const int& v = 42; }; A a1; A a2(1); — _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:

[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); A a(2); B b(3); C c(4); — _end example_]

[Note 7:

The expression-list or braced-init-listof a mem-initializeris 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​::​rto refer toX​::​a, initializesX​::​bwith the value of the constructor parameteri, initializesX​::​iwith the value of the constructor parameteri, and initializesX​::​jwith the value ofX​::​i; this takes place each time an object of classXis 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 thetypeidoperator ([expr.typeid]) or of adynamic_cast ([expr.dynamic.cast]).

However, if these operations are performed in actor-initializer(or in a function called directly or indirectly from actor-initializer) before all themem-initializer_s_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()), j(f()) { } };class C { public: C(int);};class D : public B, C { int i;public: D() : C(f()), i(f()) { } }; — _end example_]

[Note 8:

[class.cdtor] describes the results of virtual function calls,typeidanddynamic_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-listfor the class.

[Example 12: template<class... Mixins> class X : public Mixins... { public: X(const Mixins&... mixins) : Mixins(mixins)... { } }; — _end example_]

11.9.4 Initialization by inherited constructor [class.inhctor.init]

When a constructor for type B is invoked to initialize an object of a different type D(that is, when the constructor was inherited ([namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the inherited constructor if the base class subobject were to be initialized as part of the D object ([class.base.init]).

The invocation of the inherited constructor, including the evaluation of any arguments, is omitted if the B subobject is not to be initialized as part of the D object.

The complete initialization is considered to be a single function call; in particular, unless omitted, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the D object.

[Example 1: struct B1 { B1(int, ...) { } };struct B2 { B2(double) { } };int get();struct D1 : B1 { using B1::B1; int x;int y = get();};void test() { D1 d(2, 3, 4); D1 e; } struct D2 : B2 { using B2::B2; B1 b;}; D2 f(1.0); struct W { W(int); };struct X : virtual W { using W::W; X() = delete; };struct Y : X { using X::X; };struct Z : Y, virtual W { using Y::Y; }; Z z(0); template<class T> struct Log : T { using T::T; ~Log() { std::clog << "Destroying wrapper" << std::endl; } };

Class template Log wraps any class and forwards all of its constructors, while writing a message to the standard log whenever an object of class Log is destroyed.

— _end example_]

[Example 2: struct V { V() = default; V(int); };struct Q { Q(); };struct A : virtual V, Q { using V::V; A() = delete;};int bar() { return 42; } struct B : A { B() : A(bar()) {} };struct C : B {};void foo() { C c; } — _end example_]

If the constructor was inherited from multiple base class subobjects of type B, the program is ill-formed.

[Example 3: struct A { A(int); };struct B : A { using A::A; };struct C1 : B { using B::B; };struct C2 : B { using B::B; };struct D1 : C1, C2 { using C1::C1;using C2::C2;};struct V1 : virtual B { using B::B; };struct V2 : virtual B { using B::B; };struct D2 : V1, V2 { using V1::V1;using V2::V2;}; D1 d1(0); D2 d2(0); struct M { M(); M(int); };struct N : M { using M::M; };struct O : M {};struct P : N, O { using N::N; using O::O; }; P p(0); — _end example_]

When an object is initialized by an inherited constructor, initialization of the object is complete when the initialization of all subobjects is complete.

11.9.5 Construction and destruction [class.cdtor]

For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

[Example 1: struct X { int i; };struct Y : X { Y(); }; struct A { int a; };struct B : public A { int j; Y y; }; extern B bobj; B* pb = &bobj; int* p1 = &bobj.a; int* p2 = &bobj.y.i; A* pa = &bobj; B bobj; extern X xobj;int* p3 = &xobj.i; X xobj;

For another example,struct W { int j; };struct X : public virtual W { };struct Y { int* p; X x; Y() : p(&x.j) { } };

— _end example_]

During the construction of an object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor'sthispointer, the value of the object or subobject thus obtained is unspecified.

[Example 2: struct C;void no_opt(C*);struct C { int c; C() : c(0) { no_opt(this); } };const C cobj;void no_opt(C* cptr) { int i = cobj.c * 100; cptr->c = 1; cout << cobj.c * 100 << '\n';} extern struct D d;struct D { D(int a) : a(a), b(d.a) {} int a, b;}; D d = D(1); — _end example_]

To explicitly or implicitly convert a pointer (a glvalue) referring to an object of classXto a pointer (reference) to a direct or indirect base classBofX, the construction ofXand the construction of all of its direct or indirect bases that directly or indirectly derive fromBshall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.

To form a pointer to (or access the value of) a direct non-static member of an objectobj, the construction ofobjshall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.

[Example 3: struct A { };struct B : virtual A { };struct C : B { };struct D : virtual A { D(A*); };struct X { X(A*); };struct E : C, D, X { E() : D(this), X(this) {} }; — _end example_]

Member functions, including virtual functions ([class.virtual]), can be called during construction or destruction ([class.base.init]).

When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.

If the virtual function call uses an explicit class member access ([expr.ref]) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.

[Example 4: struct V { virtual void f();virtual void g();};struct A : virtual V { virtual void f();};struct B : virtual V { virtual void g(); B(V*, A*);};struct D : A, B { virtual void f();virtual void g(); D() : B((A*)this, this) { } }; B::B(V* v, A* a) { f(); g(); v->g(); a->f(); } — _end example_]

Thetypeidoperator ([expr.typeid]) can be used during construction or destruction ([class.base.init]).

Whentypeidis used in a constructor (including themem-initializer or default member initializer ([class.mem]) for a non-static data member) or in a destructor, or used in a function called (directly or indirectly) from a constructor or destructor, if the operand oftypeidrefers to the object under construction or destruction,typeidyields thestd​::​type_infoobject representing the constructor or destructor's class.

If the operand oftypeidrefers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.

dynamic_casts ([expr.dynamic.cast]) can be used during construction or destruction ([class.base.init]).

When adynamic_castis used in a constructor (including themem-initializer or default member initializer for a non-static data member) or in a destructor, or used in a function called (directly or indirectly) from a constructor or destructor, if the operand of thedynamic_castrefers to the object under construction or destruction, this object is considered to be a most derived object that has the type of the constructor or destructor's class.

If the operand of thedynamic_castrefers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, thedynamic_castresults in undefined behavior.

[Example 5: struct V { virtual void f();};struct A : virtual V { };struct B : virtual V { B(V*, A*);};struct D : A, B { D() : B((A*)this, this) { } }; B::B(V* v, A* a) { typeid(*this); typeid(*v); typeid(*a); dynamic_cast<B*>(v); dynamic_cast<B*>(a); } — _end example_]

11.9.6 Copy/move elision [class.copy.elision]

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object haveside effects.

In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object.

If the first parameter of the selected constructor is an rvalue reference to the object's type, the destruction of that object occurs when the target would have been destroyed; otherwise, the destruction occurs at the later of the times when the two objects would have been destroyed without the optimization.100

This elision of copy/move operations, calledcopy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

Copy elision is not permitted where an expression is evaluated in a context requiring a constant expression ([expr.const]) and in constant initialization ([basic.start.static]).

[Note 2:

It is possible that copy elision is performed if the same expression is evaluated in another context.

— _end note_]

[Example 1: class Thing { public: Thing();~Thing(); Thing(const Thing&);}; Thing f() { Thing t;return t;}Thing t2 = f();struct A { void *p;constexpr A(): p(this) {} };constexpr A g() { A loc;return loc;} constexpr A a; constexpr A b = g(); void h() { A c = g(); }

Here the criteria for elision can eliminate the copying of the object t with automatic storage duration into the result object for the function call f(), which is the non-local object t2.

Effectively, the construction of tcan be viewed as directly initializing t2, and that object's destruction will occur at program exit.

Adding a move constructor to Thing has the same effect, but it is the move construction from the object with automatic storage duration to t2 that is elided.

— _end example_]

[Example 2: class Thing { public: Thing();~Thing(); Thing(Thing&&);private: Thing(const Thing&);}; Thing f(bool b) { Thing t;if (b) throw t; return t; }Thing t2 = f(false); struct Weird { Weird(); Weird(Weird&);}; Weird g(bool b) { static Weird w1; Weird w2;if (b) return w1; else return w2; } int& h(bool b, int i) { static int s;if (b) return s; else return i; } decltype(auto) h2(Thing t) { return t; } decltype(auto) h3(Thing t) { return (t); } — _end example_]

[Example 3: template<class T> void g(const T&);template<class T> void f() { T x;try { T y;try { g(x); } catch (...) { if () throw x; throw y; } g(y);} catch(...) { g(x); g(y); } } — _end example_]