struct A { }; // implicitly declared A::operator= struct B : A { B& operator=(const B &); }; B& B::operator=(const B& s) { this->A::operator=(s); // well formed return *this; }
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier attribute-specifier-seqwhere the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:
struct S { S(); // declares the constructor }; S::S() { } // defines the constructor
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; // value of cobj.c is unspecified cptr->c = 1; cout << cobj.c * 100 // value of cobj.c is unspecified << '\n'; } extern struct D d; struct D { D(int a) : a(a), b(d.a) {} int a, b; }; D d = D(1); // value of d.b is unspecified
class X { public: X(int); X(const X&); X& operator=(const X&); ~X(); }; class Y { public: Y(int); Y(Y&&); ~Y(); }; X f(X); Y g(Y); void h() { X a(1); X b = f(X(2)); Y c = g(Y(3)); a = f(a); }X(2) is constructed in the space used to hold f()'s argument and Y(3) is constructed in the space used to hold g()'s argument.
struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
S* p = new S{ 1, {2,3} }; // Creates dangling reference
struct S { S(); S(int); friend S operator+(const S&, const S&); ~S(); }; S obj1; const S& cr = S(16)+S(23); S obj2;
struct X { operator int(); }; struct Y { operator X(); }; Y a; int b = a; // error, a.operator X().operator int() not tried int c = X(a); // OK: a.operator X().operator int()
struct X {
operator int();
};
struct Y : X {
operator char();
};
void f(Y& a) {
if (a) { // ill-formed: X::operator int() or Y::operator char()
}
}
struct X { X(int); X(const char*, int =0); X(int, int); }; void f(X arg) { X a = 1; // a = X(1) X b = "Jessie"; // b = X("Jessie",0) a = 2; // a = X(2) f(3); // f(X(3)) f({1, 2}); // f(X(1,2)) }
struct Z { explicit Z(); explicit Z(int); explicit Z(int, int); }; Z a; // OK: default-initialization performed Z b{}; // OK: direct initialization syntax used Z c = {}; // error: copy-list-initialization Z a1 = 1; // error: no implicit conversion Z a3 = Z(1); // OK: direct initialization syntax used Z a2(1); // OK: direct initialization syntax used Z* p = new Z(1); // OK: direct initialization syntax used Z a4 = (Z)1; // OK: explicit cast used Z a5 = static_cast<Z>(1); // OK: explicit cast used Z a6 = { 3, 4 }; // error: no implicit conversion
conversion-function-id: operator conversion-type-id
conversion-type-id: type-specifier-seq conversion-declarator
conversion-declarator: ptr-operator conversion-declaratorspecifies a conversion from X to the type specified by the conversion-type-id.
class Y { }; struct Z { explicit operator Y() const; }; void h(Z z) { Y y1(z); // OK: direct-initialization Y y2 = z; // ill-formed: copy-initialization Y y3 = (Y)z; // OK: cast notation } void g(X a, X b) { int i = (a) ? 1+a : 0; int j = (a&&b) ? a+b : i; if (a) { } }
struct S { operator auto() const { return 10; } // OK template<class T> operator auto() const { return 1.2; } // error: conversion function template };
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier attribute-specifier-seqwhere the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:
struct B { virtual ~B() { } }; struct D : B { ~D() { } }; D D_object; typedef B B_alias; B* B_ptr = &D_object; void f() { D_object.B::~B(); // calls B's destructor B_ptr->~B(); // calls D's destructor B_ptr->~B_alias(); // calls D's destructor B_ptr->B_alias::~B(); // calls B's destructor B_ptr->B_alias::~B_alias(); // calls B's destructor }
void* operator new(std::size_t, void* p) { return p; } struct X { X(int); ~X(); }; void f(X* p); void g() { // rare, specialized use: char* buf = new char[sizeof(X)]; X* p = new(buf) X(222); // use buf[] and initialize f(p); p->X::~X(); // cleanup }
class Arena; struct B { void* operator new(std::size_t, Arena*); }; struct D1 : B { }; Arena* ap; void foo(int i) { new (ap) D1; // calls B::operator new(std::size_t, Arena*) new D1[i]; // calls ::operator new[](std::size_t) new D1; // ill-formed: ::operator new(std::size_t) hidden }
class X { void operator delete(void*); void operator delete[](void*, std::size_t); }; class Y { void operator delete(void*, std::size_t); void operator delete[](void*); };
struct B {
virtual ~B();
void operator delete(void*, std::size_t);
};
struct D : B {
void operator delete(void*);
};
void f() {
B* bp = new D;
delete bp; // 1: uses D::operator delete(void*)
}
Here, storage for the non-array object of class
D
is deallocated by
D::operator delete(),
due to the virtual destructor.struct B { virtual ~B(); void operator delete[](void*, std::size_t); }; struct D : B { void operator delete[](void*, std::size_t); }; void f(int i) { D* dp = new D[i]; delete [] dp; // uses D::operator delete[](void*, std::size_t) B* bp = new D[i]; delete[] bp; // undefined behavior }
struct complex { complex(); complex(double); complex(double,double); }; complex sqrt(complex,complex); complex a(1); // initialize by a call of complex(double) complex b = a; // initialize by a copy of a complex c = complex(1,2); // construct complex(1,2) using complex(double,double), // copy/move it into c complex d = sqrt(b,c); // call sqrt(complex,complex) and copy/move the result into d complex e; // initialize by a call of complex() complex f = 3; // construct complex(3) using complex(double), copy/move it into f complex g = { 1, 2 }; // initialize by a call of complex(double, double)
complex v[6] = { 1, complex(1,2), complex(), 2 };
struct X { int i; float f; complex c; } x = { 99, 88.8, 77.7 };
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
struct A { A(); };
struct B: public virtual A { };
struct C: public A, public B { C(); };
C::C(): A() { } // ill-formed: which A?
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 };
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);
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 };
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
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()
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) { } };
class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined: 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: calls member function but base C not yet initialized i(f()) { } // well-defined: bases are all initialized };
template<class... Mixins> class X : public Mixins... { public: X(const Mixins&... mixins) : Mixins(mixins)... { } };
struct B1 { B1(int, ...) { } }; struct B2 { B2(double) { } }; int get(); struct D1 : B1 { using B1::B1; // inherits B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4), // then d.x is default-initialized (no initialization is performed), // then d.y is initialized by calling get() D1 e; // error: D1 has a deleted default constructor } struct D2 : B2 { using B2::B2; B1 b; }; D2 f(1.0); // error: B1 has a deleted default constructor 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); // OK: initialization of Y does not invoke default constructor of X template<class T> struct Log : T { using T::T; // inherits all constructors from class T ~Log() { std::clog << "Destroying wrapper" << std::endl; } };
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); // ill-formed: ambiguous D2 d2(0); // OK: initializes virtual B base class, which initializes the A base class // then initializes the V1 and V2 base classes as if by a defaulted default constructor 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); // OK: use M(0) to initialize N's base class, // use M() to initialize O's base class
struct X { int i; }; struct Y : X { Y(); }; // non-trivial struct A { int a; }; struct B : public A { int j; Y y; }; // non-trivial extern B bobj; B* pb = &bobj; // OK int* p1 = &bobj.a; // undefined, refers to base class member int* p2 = &bobj.y.i; // undefined, refers to member's member A* pa = &bobj; // undefined, upcast to a base class type B bobj; // definition of bobj extern X xobj; int* p3 = &xobj.i; // OK, X is a trivial class X xobj;
struct W { int j; };
struct X : public virtual W { };
struct Y {
int* p;
X x;
Y() : p(&x.j) { // undefined, x is not yet constructed
}
};
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), // undefined: upcast from E* to A* might use path E* → D* → A* // but D is not constructed // “D((C*)this)” would be defined: E* → C* is defined because E() has started, // and C* → A* is defined because C is fully constructed X(this) {} // defined: upon construction of X, C/B/D/A sublattice is fully constructed };
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(); // calls V::f, not A::f g(); // calls B::g, not D::g v->g(); // v is base of B, the call is well-defined, calls B::g a->f(); // undefined behavior, a's type not a base of B }
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); // type_info for B typeid(*v); // well-defined: *v has type V, a base of B yields type_info for B typeid(*a); // undefined behavior: type A not a base of B dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B results in B* dynamic_cast<B*>(a); // undefined behavior, a has type A*, A not a base of B }
struct X { X(); // default constructor X(X&); // copy constructor with a non-const parameter }; const X cx; X x = cx; // error: X::X(X&) cannot copy cx into x
struct S { template<typename T> S(T); S(); }; S g; void h() { S a(g); // does not instantiate the member template to produce S::S<S>(S); // uses the implicitly declared copy constructor }
X::X(const X&)
X::X(X&)
struct X {
X();
X& operator=(X&);
};
const X cx;
X x;
void f() {
x = cx; // error: X::operator=(X&) cannot assign cx into x
}
X& X::operator=(const X&)
X& X::operator=(X&)
struct S { int a; S& operator=(const S&) = default; };will not have a default move assignment operator implicitly declared because the copy assignment operator has been user-declared.
struct S { int a; S& operator=(const S&) = default; S& operator=(S&&) = default; };
X& X::operator=(X&&);
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 a; return a; } constexpr A a; // well-formed, a.p points to a constexpr A b = g(); // well-formed, b.p points to b void g() { A c = g(); // well-formed, c.p may point to c or to an ephemeral temporary }
class Thing { public: Thing(); ~Thing(); Thing(Thing&&); private: Thing(const Thing&); }; Thing f(bool b) { Thing t; if (b) throw t; // OK: Thing(Thing&&) used (or elided) to throw t return t; // OK: Thing(Thing&&) used (or elided) to return t } Thing t2 = f(false); // OK: no extra copy/move performed, t2 constructed by call to f struct Weird { Weird(); Weird(Weird&); }; Weird g() { Weird w; return w; // OK: first overload resolution fails, second overload resolution selects Weird(Weird&) }