Sei sulla pagina 1di 18

http://en.cppreference.

com/w/cpp/language/list_initialization
https://www.youtube.com/watch?v=7FF7oYcRGmY
http://stackoverflow.com/questions/10940951/c11-initialize-map
Initialization in C++03 is tricky, to say the least, with four different initialization notations and far too many arbitrary restrictions
and loopholes:

No way to initialize a member array

No convenient form of initializing containers

No way to initialize dynamically allocated POD types

C++11 fixes these problems with new facilities for initializing objects. In this article, I present the new C++11 brace-initialization
notation, discuss class member initialization, and explain how to initialize containers by using initializer lists.

Initializer Lists and Sequence Constructors


An initializer list lets you use a sequence of values wherever an initializer can appear. For example, you can initialize a vector in
C++11 like this:

vector<int> vi {1,2,3,4,5,6};

vector<double> vd {0.5, 1.33, 2.66};

You may include as many initializers as you like between the braces. Although superficially this new syntax seems identical to
the brace-init notation we discussed earlier, behind the scenes it's a different story. C++11 furnishes every STL container with a
new constructor type called a sequence constructor. A sequence constructor intercepts initializers in the form of {x,y...}. To
make this machinery work, C++11 introduced another secret ingredient: an auxiliary class template
calledstd::initializer_list<T>. When the compiler sees an initializer list, say {0.5, 1.33, 2.66}, it transforms
the values in that list into an array of T with n elements (n is the number of values enclosed in braces) and uses that array to
populate an implicitly generated initializer_list<T> object. The class template initializer_list has three
member functions that access the array:

template<class E> class initializer_list

//implementation (a pair of pointers or a pointer + length)

public:

constexpr initializer_list(const E*, const E*); // [first,last)

constexpr initializer_list(const E*, int); // [first, first+length)

constexpr int size() const; // no. of elements

constexpr const T* begin() const; // first element

constexpr const T* end() const; // one more than the last element

};

To better understand how the compiler handles initializer lists of containers, let's dissect a concrete example. Suppose your code
contains the following declaration of a vector:

vector<double> vd {0.5, 1.33, 2.66};

The compiler detects the initializer list {0.5, 1.33, 2.66} and performs the following steps:
1.

Detect the type of the values in the initializer list. In the case of {0.5, 1.33, 2.66}, the type is double.

2.

Copy the values from the list into an array of three doubles.

3.

Construct an initializer_list<double> object that "wraps" the array created in the preceding step.

4.

Pass the initializer_list<double> object by reference to vd's sequence constructor. The constructor in turn
allocates n elements in the vector object, initializing them with the values of the array.

It's hard to imagine that so much is going on behind the scenes every time you initialize an STL container with a pair of braces!
The good news is that you don't have to do anything for this magic to happenit just works. Of course, you still need a C++11compliant compiler as well as a C++11-compliant Standard Library to use initializer lists. Make sure that your target project is
built with the appropriate compilation options, too.

In Conclusion
The C++ standards committee invested a lot of time and effort in finding a solution to the limitations of C++03 initialization. It
looks like they succeeded. Historically, brace-init, class member initializers, and initializer lists were three independent
proposals. Later, they were revised to ensure compatibility and uniformity. Together, these three initialization-related features

make C++11 programming simpler and more intuitive. You will surely appreciate them next time you initialize a dynamically
allocated arrayor, indeed, a vector.

Initializer lists
The containers in C++03 could not be initialized as "naturally" as good old C-style arrays. That has changed.
Example:

1 // C arrays
2 char
array1[] = {'A', 'B'};
3 double array2[] = {32.0, 6.003, -0.1};
4
5 // C++03 vectors
6
7 std::vector<char> cpp03vector1;
8 cpp03vector1.push_back('A');
9 cpp03vector1.push_back('B');
10
11 std::vector<double> cpp03vector2(3);
12 cpp03vector2[0] = 32.0;
13 cpp03vector2[1] = 6.003;
14 cpp03vector2[2] = -0.1;
15
16 // C++11 vectors
17 std::vector<char>
cpp11vector1 = {'A', 'B'};
18 std::vector<double> cpp11vector2 = {32.0, 6.003, -0.1};
19 // or...
20 std::vector<char>
cpp11vector3{'A', 'B'};
21 std::vector<double> cpp11vector4{32.0, 6.003, -0.1};
22 // notice that this works for all other containers as well, not just std::vector
Initializer lists can also be used for more complex structures.
Example:

1 #include <map>
2 #include <string>
3 #include <vector>
4 #include <utility>
5
6 usingnamespace std;
7
8 map<string, vector<pair<string, int>>> name_languages_year {
9
{"Dennis Ritchie",
{{"B",
1969}, {"C",
1973}}},
10
{"Niklaus Wirth",
{{"Pascal", 1970}, {"Modula-2", 1973}, {"Oberon", 1986}}},
11
{"Bjarne Stroustrup", {{"C++",
1983}}},
12
{"Walter Bright",
{{"D",
1999}}}
13 };
14 // notice how the lists are nested to match the templates' parameters
15
16 cout << name_languages_year["Niklaus Wirth"].at(0).first << endl; // prints `Pascal'
17
18 // adds a new entry to the map
19 name_languages_year["John McCarthy"] = {
20
{"Lisp", 1958}
21 };
22 // notice the lack of explicit types

C++ arrays

This is more of an addition than an improvement, but I decided to include it in the article anyway.
C++11 provides std::array, which has the purpose of replacing C arrays. It is a fixed-sized, lightweight alternative
to the dynamically-sized std::vector.
Example:

1 #include <array>
2
3 // C arrays
4 char carray1[] = "Abc"; // caution, an unseen '\0' is added to the end
5 float carray2[] = {0.2f, 33.33f};
6
7 // C++ arrays
8 std::array<char, 3> cpparray1{{'A', 'b', 'c'}};
9 std::array<float, 2> cpparray2{{0.2f, 33.33f}};
10 // observation 1: the size must be deducible at compile time
11 // observation 2: the array cannot be resized
12 // observation 3: the inner braces are due to the nature of initializer lists,
13 // think of it as one list per template parameter
14
15 // array test drive: the old versus the new
16
17 std::cout <<sizeof carray1 - 1; // -1 because of the extra '\0'
18 std::cout <<sizeof carray2 / sizeof (float); // because number of elements != number
19 of bytes
20 std::cout << cpparray1.size();
21 std::cout << cpparray2.size();
22
23 carray2[-5] = 0.1f; // do the moonwalk!
24 cpparray2.at(-5) = 0.1f; // throws std::out_of_range exception
25
26 // of course there are more reasons why C++ arrays are better than C arrays
// but this example code section is already too big...

list initialization (since C++11)


Initializes an object from braced-init-list

Syntax direct-list-initialization

T object { arg1, arg2, ... };

(1)

T { arg1, arg2, ... };

(2)

new T { arg1, arg2, ... }

(3)

Class { T member { arg1, arg2, ... }; };

(4)

Class::Class() : member{arg1, arg2, ...} {...

(5)

copy-list-initialization

T object = {arg1, arg2, ...};

(6)

function( { arg1, arg2, ... } ) ;

(7)

return { arg1, arg2, ... } ;

(8)

object[ { arg1, arg2, ... } ] ;

(9)

object = { arg1, arg2, ... } ;

(10)

U( { arg1, arg2, ... } )

(11)

Class { T member = { arg1, arg2, ... }; };

(12)

List initialization is performed in the following situations:

direct-list-initialization (both explicit and non-explicit constructors are considered)

1) initialization of a named variable with a braced-init-list (that is, a possibly empty brace-enclosed list of expressions or
nested braced-init-lists)

2) initialization of an unnamed temporary with a braced-init-list

3) initialization of an object with dynamic storage duration with a new-expression, where the initializer is a brace-init-list

4) in a non-static data member initializer that does not use the equals sign

5) in a member initializer list of a constructor if braced-init-list is used

copy-list-initialization (only non-explicit constructors are considered)

6) initialization of a named variable with a braced-init-list after an equals sign

7) in a function call expression, with braced-init-list used as an argument and list-initialization initializes the function
parameter

8) in a return statement with braced-init-list used as the return expression and list-initialization initializes the returned
object

9) in a subscript expression with a user-defined operator[], where list-initialization initializes the parameter of the
overloaded operator

10) in an assignment expression, where list-initialization initializes the parameter of the overloaded operator=

11) functional cast expression or other constructor invocations, where braced-init-list is used in place of a constructor
argument. Copy-list-initialization initializes the constructor's parameter (note; the type U in this example is not the type that's
being list-initialized; U's constructor's parameter is)

12) in a non-static data member initializer that uses the equals sign

Explanation
The effects of list initialization of an object of type T are:

If T is a class type and the initializer list has a single element of the same or derived type
(possibly cv-qualified), the object is initialized from that element (by copy-initialization for copylist-initialization, or by direct-initialization for direct-list-initialization).

(since C+
+17)

Otherwise, if T is a character array and the initializer list has a single element that is an
appropriately-typed string literal, the array is initialized from the string literal as usual

If the braced-init-list is empty and T is a class type with a default constructor, valueinitialization is performed.

Otherwise, if T is an aggregate type, aggregate initialization is performed.

If T is an aggregate type, aggregate initialization is performed.

Otherwise, If the braced-init-list is empty and T is a class type with a default

(until C+
+14)

(since C+
+14)

constructor, value-initialization is performed.

Otherwise, if T is a specialization of std::initializer_list, a new std::initializer_list prvalue of the same type is


constructed and used to direct-initialize or copy-initialize the object of type T, depending on context.

Otherwise, the constructors of T are considered, in two phases:

All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining
arguments have default values, are examined, and matched by overload resolution against a single argument of
type std::initializer_list

If the previous stage does not produce a match, all constructors of T participate in overload resolution against the
set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing
conversions are allowed. If this stage produces an explicit constructor as the best match for a copy-list-initialization,
compilation fails (note, in simple copy-initialization, explicit constructors are not considered at all)

Otherwise, if the braced-init-list has only one element and either T isn't a reference type or is reference type that
isn't compatible with the type of the element, T is direct-initialized (in direct-list-initialization) or copy-initialized (in
copy-list-initialization), except that narrowing conversions are not allowed.

Otherwise, if T is reference type, a prvalue temporary of the referenced type is list-initialized, and the reference is
bound to that temporary. (this fails if the reference is a non-const lvalue reference)

Otherwise, if the braced-init-list has no elements, T is value-initialized.

Narrowing conversions
list-initialization limits the allowed implicit conversions by prohibiting the following:

conversion from a floating-point type to an integer type


conversion from a long double to double or to float and conversion from double to float, except where
the source is a constant expression whose value can be stored exactly in the target type

conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the
original, except where source is a constant expression whose value can be stored exactly in the target type

Notes
A braced-init-list is not an expression and therefore has no type, e.g. decltype({1,2}) is ill-formed. Having no type
implies that template type deduction cannot deduce a type that matches a braced-init-list, so given the
declaration template<class T> void f(T); the expression f({1,2,3}) is ill-formed. A special exception is made
for type deduction using the keyword auto , which deduces any braced-init-list as std::initializer_list
Also because braced-init-list has no type, special rules for overload resolution apply when it is used as an argument to
an overloaded function call.

Example
#include <iostream>
#include <vector>
#include <map>
#include <string>
struct Foo {
std::vector<int> mem ={1,2,3};// list-initialization of a non-static member
std::vector<int> mem2;
Foo(): mem2{-1, -2, -3}{}// list-initialization of a member in constructor
};
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
return{p.second, p.first};// list-initialization in return statement
}
int main()
{
int n0{};// value-initialization (to zero)
int n1{1};// direct-list-initialization
std::string s1{'a', 'b', 'c', 'd'};// initializer-list constructor call
std::string s2{s1, 2, 2};// regular constructor call
std::string s3{0x61, 'a'};// initializer-list ctor is preferred to (int, char)
int n2 ={1};// copy-list-initialization
double d =double{1.2};// list-initialization of a temporary, then copy-init
std::map<int, std::string> m ={// nested list-initialization
{1, "a"},

{2, {'a', 'b', 'c'}},


{3, s1}
};
std::cout<< f({"hello", "world"}).first// list-initialization in function call
<<'\n';
constint(&ar)[2]={1,2};// binds a lvalue reference to a temporary array
int&& r1 ={1};// binds a rvalue reference to a temporary int
//

int& r2 = {2}; // error: cannot bind rvalue to a non-const lvalue ref

//

int bad{1.0}; // error: narrowing conversion

unsignedchar uc1{10};// okay


//

unsigned char uc2{-1}; // error: narrowing conversion


Foo f;

std::cout<< n0 <<' '<< n1 <<' '<< n2 <<'\n'


<< s1 <<' '<< s2 <<' '<< s3 <<'\n';
for(auto p: m)
std::cout<< p.first<<' '<< p.second<<'\n';
for(auto n: f.mem)
std::cout<< n <<' ';
for(auto n: f.mem2)
std::cout<< n <<' ';
}
Output:
world
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3

default initialization
This is the initialization performed when a variable is constructed with no initializer.

Syntax

T object ;

(1)

new T ;
new T ( ) ; (until c++03)

(2)

Explanation
Default initialization is performed in three situations:
1) when a variable with automatic, static, or thread-local storage duration is declared with no initializer.
2) when an object with dynamic storage duration is created by a new-expression with no initializer or when an object is created
by a new-expression with the initializer consisting of an empty pair of parentheses

(until C++03) .

3) when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.
The effects of default initialization are:

If T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolutionagainst
the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial
value for the new object.

If T is an array type, every element of the array is default-initialized.

Otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to
indeterminate values.

Only (possibly cv-qualified) non-POD class types (or arrays thereof) with automatic storage duration were considered to be
default-initialized when no initializer is used. Scalars and POD types with dynamic storage duration were considered to be not

(until C+
+11)

initialized (since C++11, this situation was reclassified as a form of default initialization).

Prior to C++03 (which introduced value initialization), the expression new T() as well as a member initializer naming a base

(until C+
+03)

or a member with the initializer in the form of an empty pair of parentheses was classified as default initialization, but
specified zero initialization for non-class types.

If T is a cv-qualified type, its cv-unqualified version is used for the purpose of default-initialization.

(until C+
+11)

Use of an indeterminate value obtained by default-initializing a non-class variable of any type is undefined behavior,
except in the following cases:
if the indeterminate value of unsigned narrow character type is assigned to another

variable with unsigned narrow character type (the value of the variable becomes
indeterminate, but the behavior is not undefined)
if the indeterminate value of unsigned narrow character type is used to initialize another

variable with unsigned narrow character type


if the indeterminate value of unsigned narrow character type results from

the second or third operand of a conditional expression

the right operand of the comma operator

the operand of a cast or conversion to an unsigned narrow character type

a discarded-value expression

(since C+
+14)

int f(bool b)
{
int x;// value of x is indeterminate
int y = x;// undefined behavior
unsignedchar c;// value of c is indeterminate
unsignedchar d = c;// OK, value of d is indeterminate
int e = d;// undefined behavior
return b ? d :0;// undefined behavior if b is true
}

Notes
Default initialization of non-class variables with automatic and dynamic storage duration produces objects with indeterminate
values (static and thread-local objects get zero initialized)
If T is a const-qualified type, it must be a class type with a user-provided default constructor.
Reference cannot be default-initialized.

Example
#include <string>
struct T1 {};

class T2 {
int mem;
public:
T2(){}// "mem" not in initializer list
};
int n;// A two-phase initialization is done
// In the first phase, zero initialization initializes n to zero
// In the second phase, default initialization does nothing, leaving n being zero
int main()
{
int n;// non-class: the value is indeterminate
std::string s;// calls default ctor, the value is "" (empty string)
std::string a[2];// calls default ctor, creates two empty strings
//

int& r;

// error: default-initializing a reference

//

const int n;

// error: const non-class type

//

const T1 nd;

// error: const class type with implicit ctor

T1 t1;// ok, calls implicit default ctor


const T2 t2;// ok, calls the user-provided default ctor
// t2.mem is default-initialized (to indeterminate value)
}

direct initialization
Initializes an object from explicit set of constructor arguments.

Syntax

T object ( arg );
T object ( arg1, arg2, ... );

(1)

T object { arg };
T object { arg1, arg2, ... };

(2)

T ( other )
T ( arg1, arg2, ... );

static_cast< T >( other )

(3)

(4)

(since C++11)

new T(args, ...)

(5)

Class::Class() : member(args, ...) {...

(6)

[arg](){...

(7)

(since C++11)

Explanation
Direct initialization is performed in the following situations:
1) initialization with a nonempty parenthesized list of expressions
2) during list-initialization sequence, if no initializer-list constructors are provided and a matching constructor is accessible, and
all necessary implicit conversions are non-narrowing.
3) initialization of a prvalue temporary by functional cast or with a parenthesized expression list
4) initialization of a prvalue temporary by a static_cast expression
5) initialization of an object with dynamic storage duration by a new-expression with a non-empty initialize
6) initialization of a base or a non-static member by constructor initializer list
7) initialization of closure object members from the variables caught by copy in a lambda-expression
The effects of direct initialization are:
If T is a class type, the constructors of T are examined and the best match is selected by overload resolution. The

constructor is then called to initialize the object.


Otherwise, if T is a non-class type, standard conversions are used, if necessary, to convert the value of other to the cv-

unqualified version of T.

Notes
Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and
user-defined conversion functions, while direct-initialization considers all constructors and implicit conversion sequences.

Example
#include <string>
#include <iostream>
#include <memory>
struct Foo {
int mem;
explicit Foo(int n): mem(n){}
};

int main()
{
std::string s1("test");// constructor from const char*
std::string s2(10, 'a');
std::unique_ptr<int> p(new int(1));// OK: explicit constructors allowed
//

std::unique_ptr<int> p = new int(1); // error: constructor is explicit


Foo f(2);// f is direct-initialized:

// constructor parameter n is copy-initialized from the rvalue 2


// f.mem is direct-initialized from the parameter n
//

Foo f2 = 2; // error: constructor is explicit

std::cout<< s1 <<' '<< s2 <<' '<<*p <<' '<< f.mem<<'\n';


}
Output:
test aaaaaaaaaa 1 2

aggregate initialization
Initializes an aggregate from braced-init-list

Syntax

T object = {arg1, arg2, ...};

(1)

T object {arg1, arg2, ...};

(2)

(since C++11)

Explanation
Aggregate initialization is a form of list-initialization, which initializes aggregates
An aggregate is an object of the type that is one of the following

array type
class type (typically, struct or union), that has

no private or protected members

no user-provided constructors (explicitly defaulted or deleted constructors are allowed)

no base classes

no virtual member functions

(since C++11)

no brace-or-equal initializers for non-static members

(since C++11)
(until C++14)

The effects of aggregate initialization are:

Each array element or non-static class member, in order of array subscript/appearance in the class definition, iscopyinitialized from the corresponding clause of the initializer list.

If the initializer clause is an expression, implicit conversions are allowed as per copy-initialization, except

(since C++11)

if

they are narrowing (as in list-initialization).

If the initializer clause is a nested braced-init-list (which is not an expression and has no type), the corresponding class
member is itself an aggregate: aggregate initialization is recursive.

If the object is an array of unknown size, and the supplied brace-enclosed initializer list has n clauses, the size of the
array is n

Static data members and anonymous bit-fields are skipped during aggregate initialization.

If the number of initializer clauses exceeds the number of members to initialize, the program is ill-formed (compiler

error)
If the number of initializer clauses is less than the number of members or initializer clauses is completely empty, the
remaining members are initialized by their brace-or-equal initializers, if provided in the class definition, and
otherwise (since C++14) by empty lists, which performs value-initialization. If a member of a reference type is one of

these remaining members, the program is ill-formed (references cannot be value-initialized)


If the aggregate initialization uses the form with the equal sign ( T a = {args..} ), (until C++14) the braces around the
nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to
initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used
to initialize the following members of the object. However, if the object has a sub-aggregate without any members (an
empty struct, or a struct holding only static members), brace elision is not allowed, and an empty nested list {} must
be used.

When a union is initialized by aggregate initialization, only its first non-static data member is initialized.

Character arrays
Arrays of character types (char, signed char, unsigned char, char16_t, char32_t, wchar_t) can be initialized
from an appropriate string literal, optionally enclosed in braces. Successive characters of the string literal (which includes
the implicit terminating null character) initialize the elements of the array. If the size of the array is specified and it is larger
than the number of characters in the string literal, the remaining characters are zero-initialized.
char a[]="abc";
// equivalent to char a[4] = {'a', 'b', 'c', '\0'};
//

unsigned char b[3] = "abc"; // Error: initializer string too long

unsignedchar 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'};

Notes
Until C++11, narrowing conversions were permitted in aggregate initialization, but they are no longer allowed.

Until C++11, aggregate initialization could not be used in a constructor initializer list due to syntax restrictions.
Until C++14, the direct-initialization form T a {args..} did not permit brace elision.
In C, character array of size one less than the size of the string literal may be initialized from a string literal; the resulting
array is not null-terminated. This is not allowed in C++.

Example
#include <string>
#include <array>
struct S {
int x;
struct Foo {
int i;
int j;
int a[3];
} b;
};
union U {
int a;
constchar* 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]
//

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

copy initialization
Initializes an object from another object

Syntax

T object = other;

(1)

f(other)

(2)

return other;

(3)

throw object;
catch (T object)

T array[N] = {other};

(4)

(5)

Explanation
Copy initialization is performed in the following situations:
1) when a named variable (automatic, static, or thread-local) of a non-reference type T is declared with the initializer consisting
of an equals sign followed by an expression.
2) when passing an argument to a function by value

3) when returning from a function that returns by value


4) when throwing or catching an exception by value
5) as part of aggregate initialization, to initialize each element for which an initializer is provided
The effects of copy initialization are:
If T is a class type and the type of other is cv-unqualified version of T or a class derived from T, the converting

constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to
initialize the object.
If T is a class type, and the type of other is different, or if T is non-class type, but the type of other is a class type,user-

defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type
and a conversion function is available) are examined and the best one is selected through overload resolution. The result of
the conversion, which is a prvalue temporary if a converting constructor was used, is then used to direct-initialize the object.
The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the
target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used.
Otherwise (if neither T nor the type of other are class types), standard conversions are used, if necessary, to convert the

value of other to the cv-unqualified version of T.

Notes
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not
considered for copy-initialization.
If other is an rvalue expression, move constructor will be selected by overload resolution and called during copy-initialization.
There is no such term as move-initialization.
Implicit conversion is defined in terms of copy-initialization: if an object of type T can be copy-initialized with expression E,
then E is implicitly convertible to T.
The equals sign, =, in copy-initialization of a named variable is not related to the assignment operator. Assignment operator
overloads have no effect on copy-initialization.

Example
#include <string>
#include <utility>
#include <memory>
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
constint b = n;// const doesn't matter
int c = b;// ...either way

Potrebbero piacerti anche