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

佐手、 提交于 2020-08-18 00:05:32

问题


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?


回答1:


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.




回答2:


I vastly prefer copy-list initialization in general (and particularly in the case of std::vector) because direct-list initialization is very similar at a glance to std::vector's actual constructors when there is only one or two elements.

std::vector<int> x(2);
// is very different from
std::vector<int> x{2};

Being explicit about the fact that I'm assigning an initial value to the vector and not configuring it with the values is less prone to misreadings.



来源:https://stackoverflow.com/questions/50422129/differences-between-direct-list-initialization-and-copy-list-initialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!