Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences between direct-list-initialization and copy-list-initialization

Tags:

I would like to know if there is any difference the following two types of std::vector initialization in C++11 and later.

std::vector<int> v1 {1, 2, 3, 4, 5}; std::vector<int> v2 = {1, 2, 3, 4, 5}; 

Here is a complete code example that works fine.

#include <iostream> #include <vector>  int main() {     std::vector<int> v1 {1, 2, 3, 4, 5};     std::vector<int> v2 = {1, 2, 3, 4, 5};      std::cout << v1.size() << '\n';     std::cout << v2.size() << '\n'; } 

I see both initializations leading to identical results.

The example at http://en.cppreference.com/w/cpp/container/vector uses the second kind, so that got me thinking if this kind of initialization has any advantage.

In general, I want to know if one initialization has a specific technical advantage over the other, or if one initialization is considered best-practice while the other is not, and if so why.

Especially, what I am concerned about is whether the copy-list-initialization has additional overhead due to temporary objects and copying?

like image 664
Lone Learner Avatar asked May 19 '18 05:05

Lone Learner


People also ask

What is copy list initialization?

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy initialization.

What is direct initialization?

Direct Initialization or Assignment Operator (Syntax) This assigns the value of one object to another object both of which are already exists. Copy initialization is used when a new object is created with some existing object. This is used when we want to assign existing object to new object.

How do you initialize a list in C++?

In C++11 and above, we can use the initializer lists '{...}' to initialize a list. This won't work in C++98 as standard permits list to be initialized by the constructor, not by '{...}' .

What is uniform initialization in C++?

Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces ({}) to enclose initializer values.

What is the difference between copy initialization and direct initialization?

On the other hand, the direct initialization can be done using assignment operation. The main difference between these two types of initialization is that the copy initialization creates a separate memory block for the new object.

What are the two types of initialization in C++?

The two forms of initialization are direct and copy initialization: Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of T (including explicit ones), and the argument is x.

What is copy initialization in C++?

An initialization with an = is considered a copy initialization. In principle, a copy of the initializer (the object we are copying from) is placed into the initialized object. However, such a copy may be optimized away (elided), and a move operation (based on move semantics) may be used if the initializer is an rvalue.

What is the difference between Default initialization and value initialization?

The first default-initializes if A is a non-POD, and doesn't do any initialization for a POD (Read 8.6/9 ). The second copy initializes: Value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.6/14 ). This of course will require a non-explicit copy constructor (Read 8.6/14 and 12.3.1/3 and 13.3.1.3/1 ).


1 Answers

List initialization is informally called "uniform initialization" because its meaning and behavior is intended to be the same regardless of how you invoke it.

Of course, C++ being C++, what is "intended" doesn't always happen.

There are basically three major differences between the behavior of direct-list-initialization and copy-list-initialization. The first is the one you will encounter most frequently: if list initialization would call a constructor marked explicit, then there is a compile error if the list-initialization form is copy-list-initialization.

This behavioral difference is defined in [over.match.list]/1:

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

That's a function of overload resolution.

The second major difference (new to C++17) is that, given an enum-type with a fixed underlying size, you can perform direct-list-initialization on it with a value of the underlying type. But you cannot perform copy-list-initialization from such a value. So enumeration e{value}; works, but not enumeration e = {value};.

The third major difference (also new to C++17) relates to the behavior of braced-init-lists in auto deduction. Normally, auto behaves not too distinctly from template argument deduction. But unlike template argument deduction, auto can be initialized from a braced-init-list.

If you initialize an auto variable using direct-list-initialization with a single expression in the list, the compiler will deduce the variable to be of the type of the expression:

auto x{50.0f}; //x is a `float`. 

Sounds reasonable. But if do the exact same thing with copy-list-initialization, it will always be deduced to an initializer_list<T>, where T is the type of the initializer:

auto x = {50.0f}; //x is an `initializer_list<float>` 

So very, very uniform. ;)

Thankfully, if you use multiple initializers in the braced-init-list, direct-list-initialization for an auto-deduced variable will always give a compile error, while copy-list-initialization will just give a longer initializer_list. So auto-deduced direct-list-initialization will never give an initializer_list, while auto-deduced copy-list-initialization always will.

There are some minor differences that rarely affects the expected behavior of the initialization. These are cases where list-initialization from a single value will use copy or direct (non-list) initialization as appropriate to the list-initialization form. These cases are:

  1. Initializing an aggregate from a single value which is the same type as the aggregate being initialized. This bypasses aggregate initialization.

  2. Initializing a non-class, non-enumeration type from a single value.

  3. Initializing a reference.

Not only do these not happen particularly frequently, they basically never really change the meaning of the code. Non-class types don't have explicit constructors, so the difference between copy and direct initialization is mostly academic. Same goes for references. And the aggregate case is really just about performing a copy/move from a given value.

like image 110
Nicol Bolas Avatar answered Oct 29 '22 23:10

Nicol Bolas