Default initialization
struct T1 { int mem; };
struct T2
{
int mem;
T2() { } // "mem" is not in the initializer list
};
int n; // static non-class, a two-phase initialization is done:
// 1) zero initialization initializes n to zero
// 2) default initialization does nothing, leaving n being zero
int main()
{
int n; // non-class, the value is indeterminate
std::string s; // class, calls default ctor, the value is "" (empty string)
std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
// int& r; // error: a reference
// const int n; // error: a const non-class
// const T1 t1; // error: const class with implicit default ctor
T1 t1; // class, calls implicit default ctor
const T2 t2; // const class, calls the user-provided default ctor
// t2.mem is default-initialized (to indeterminate value)
}
Value initialization
T()
new
T ()
Class::
Class(
…)
:
member()
{
… }
T object {};
T{}
new
T {}
Class::
Class(
…)
:
member{}
{
… }
struct T1
{
int mem1;
std::string mem2;
}; // implicit default constructor
struct T2
{
int mem1;
std::string mem2;
T2(const T2&) { } // user-provided copy constructor
}; // no default constructor
struct T3
{
int mem1;
std::string mem2;
T3() { } // user-provided default constructor
};
std::string s{}; // class => default-initialization, the value is ""
int main()
{
int n{}; // scalar => zero-initialization, the value is 0
double f = double(); // scalar => zero-initialization, the value is 0.0
int* a = new int[10](); // array => value-initialization of each element
// the value of each element is 0
T1 t1{}; // class with implicit default constructor =>
// t1.mem1 is zero-initialized, the value is 0
// t1.mem2 is default-initialized, the value is ""
// T2 t2{}; // error: class with no default constructor
T3 t3{}; // class with user-provided default constructor =>
// t3.mem1 is default-initialized to indeterminate value
// t3.mem2 is default-initialized, the value is ""
std::vector<int> v(3); // value-initialization of each element
// the value of each element is 0
std::cout << s.size() << ' ' << n << ' ' << f << ' ' << a[9] << ' ' << v[2] << '\n';
std::cout << t1.mem1 << ' ' << t3.mem1 << '\n';
delete[] a;
}
Direct initialization
T object ( arg ); T object ( arg1, arg2, … ); |
T object { arg }; |
T ( other ) T ( arg1, arg2, … ) |
static_cast< T >( other ) |
new T( args, …) |
Class:: Class() : member( args, …) { … } |
[ arg](){ … } |
struct B {
int a;
int&& r;
};
int f();
int n = 10;
B b1{1, f()}; // OK, lifetime is extended
B b2(1, f()); // well-formed, but dangling reference
B b3{1.0, 1}; // error: narrowing conversion
B b4(1.0, 1); // well-formed, but dangling reference
B b5(1.0, std::move(n)); // OK
struct M { };
struct L { L(M&); };
M n;
void f() {
M(m); // declaration, equivalent to M m;
L(n); // ill-formed declaration
L(l)(m); // still a declaration
}
Copy initialization
T object = other; |
T object = { other} ; |
f( other) |
return other; |
throw object; catch ( T object) |
T array[ N] = { other}; |
struct A
{
operator int() { return 12;}
};
struct B
{
B(int) {}
};
int main()
{
std::string s = "test"; // OK: constructor is non-explicit
std::string s2 = std::move(s); // this copy-initialization performs a move
// std::unique_ptr<int> p = new int(1); // error: constructor is explicit
std::unique_ptr<int> p(new int(1)); // OK: direct-initialization
int n = 3.14; // floating-integral conversion
const int b = n; // const doesn't matter
int c = b; // ...either way
A a;
B b0 = 12;
// B b1 = a; //< error: conversion from 'A' to non-scalar type 'B' requested
B b2{a}; // < identical, calling A::operator int(), then B::B(int)
B b3 = {a}; // <
auto b4 = B{a}; // <
// b0 = a; //< error, assignment operator overload needed
}
Aggregate initialization
T object = { arg1, arg2, …}; |
T object { arg1, arg2, …}; |
T object = { . designator = arg1 , . designator { arg2 } … }; |
T object { . designator = arg1 , . designator { arg2 } … }; |
T object ( arg1, arg2, …); |
struct A { int x; int y; int z; };
A a{.y = 2, .x = 1}; // error; designator order does not match declaration order
A b{.x = 1, .z = 2}; // ok, b.y initialized to 0
union u { int a; const char* b; };
u f = { .b = "asdf" }; // OK, active member of the union is b
u g = { .a = 1, .b = "asdf" }; // Error, only one initializer may be provided
struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5}; // valid C, invalid C++ (array)
struct B b = {.a.x = 0}; // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed)
char a[] = "abc";
// equivalent to char a[4] = {'a', 'b', 'c', '\0'};
// unsigned char b[3] = "abc"; // Error: initializer string too long
unsigned char b[5]{"abc"};
// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};
wchar_t c[] = {L"кошка"}; // optional braces
// equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
struct S {
int x;
struct Foo {
int i;
int j;
int a[3];
} b;
};
union U {
int a;
const char* b;
};
int main()
{
S s1 = { 1, { 2, 3, {4, 5, 6} } };
S s2 = { 1, 2, 3, 4, 5, 6}; // same, but with brace elision
S s3{1, {2, 3, {4, 5, 6} } }; // same, using direct-list-initialization syntax
S s4{1, 2, 3, 4, 5, 6}; // error in C++11: brace-elision only allowed with equals sign
// okay in C++14
int ar[] = {1,2,3}; // ar is int[3]
int ab[] (1, 2, 3); // (C++20) ab is int[3]
// char cr[3] = {'a', 'b', 'c', 'd'}; // too many initializer clauses
char cr[3] = {'a'}; // array initialized as {'a', '\0', '\0'}
int ar2d1[2][2] = {{1, 2}, {3, 4}}; // fully-braced 2D array: {1, 2}
// {3, 4}
int ar2d2[2][2] = {1, 2, 3, 4}; // brace elision: {1, 2}
// {3, 4}
int ar2d3[2][2] = {{1}, {2}}; // only first column: {1, 0}
// {2, 0}
std::array<int, 3> std_ar2{ {1,2,3} }; // std::array is an aggregate
std::array<int, 3> std_ar1 = {1, 2, 3}; // brace-elision okay
int ai[] = { 1, 2.0 }; // narrowing conversion from double to int:
// error in C++11, okay in C++03
std::string ars[] = {std::string("one"), // copy-initialization
"two", // conversion, then copy-initialization
{'t', 'h', 'r', 'e', 'e'} }; // list-initialization
U u1 = {1}; // OK, first member of the union
// U u2 = { 0, "asdf" }; // error: too many initializers for union
// U u3 = { "asdf" }; // error: invalid conversion to int
}
// aggregate
struct base1 { int b1, b2 = 42; };
// non-aggregate
struct base2 {
base2() : b3(42) {}
int b3;
};
// aggregate in C++17
struct derived : base1, base2 { int d; };
derived d1{ {1, 2}, { }, 4}; // d1.b1 = 1, d1.b2 = 2, d1.b3 = 42, d1.d = 4
derived d2{ { }, { }, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4
List initialization
struct X {
X() = default;
X(const X&) = default;
};
struct Q {
Q() = default;
Q(Q const&) = default;
Q(std::initializer_list<Q>) {}
};
int main() {
X x;
X x2 = X { x }; // copy-constructor (not aggregate initialization)
Q q;
Q q2 = Q { q }; // initializer-list constructor (not copy constructor)
}
Reference initialization
T & ref = object ; T & ref = { arg1, arg2, … }; T & ref ( object ) ; T & ref { arg1, arg2, … } ; |
T && ref = object ; T && ref = { arg1, arg2, … }; T && ref ( object ) ; T && ref { arg1, arg2, … } ; |
given R fn ( T & arg ); or R fn ( T && arg ); fn ( object ) fn ( { arg1, arg2, … } ) |
inside T & fn () or T && fn () return object ; |
given T & ref ; or T && ref ; inside the definition of Class Class:: Class(…) : ref( object) {…} |
struct S {
int mi;
const std::pair<int, int>& mp; // reference member
};
void foo(int) {}
struct A {};
struct B : A {
int n;
operator int&() { return n; }
};
B bar() { return B(); }
//int& bad_r; // error: no initializer
extern int& ext_r; // OK
int main() {
// Lvalues
int n = 1;
int& r1 = n; // lvalue reference to the object n
const int& cr(n); // reference can be more cv-qualified
volatile int& cv{n}; // any initializer syntax can be used
int& r2 = r1; // another lvalue reference to the object n
// int& bad = cr; // error: less cv-qualified
int& r3 = const_cast<int&>(cr); // const_cast is needed
void (&rf)(int) = foo; // lvalue reference to function
int ar[3];
int (&ra)[3] = ar; // lvalue reference to array
B b;
A& base_ref = b; // reference to base subobject
int& converted_ref = b; // reference to the result of a conversion
// Rvalues
// int& bad = 1; // error: cannot bind lvalue ref to rvalue
const int& cref = 1; // bound to rvalue
int&& rref = 1; // bound to rvalue
const A& cref2 = bar(); // reference to A subobject of B temporary
A&& rref2 = bar(); // same
int&& xref = static_cast<int&&>(n); // bind directly to n
// int&& copy_ref = n; // error: can't bind to an lvalue
double&& copy_ref = n; // bind to an rvalue temporary with value 1.0
// Restrictions on temporary lifetimes
std::ostream& buf_ref = std::ostringstream() << 'a'; // the ostringstream temporary
// was bound to the left operand of operator<<
// but its lifetime ended at the semicolon
// so buf_ref is a dangling reference
S a {1, {2, 3} }; // temporary pair {2, 3} bound to the reference member
// a.mp and its lifetime is extended to match a
S* p = new S{1, {2, 3} }; // temporary pair {2, 3} bound to the reference
// member p->mp, but its lifetime ended at the semicolon
// p->mp is a dangling reference
delete p;
}
examples
struct A{
A(int i){
}
};
struct B{
B(A a){
}
};
struct C{
C(B b){
}
};
int main(int argc,char* argv[]){
B b=(A)5;
B bb(5);
C c((A)5);
return 0;
}