Why doesn’t the construction move when initializing the vector from the list of initializers (via the implicit constructor)

To demonstrate the semantics of movement, I wrote the following code example with an implicit constructor from int.

struct C { int i_=0; C() {} C(int i) : i_( i ) {} C( const C& other) :i_(other.i_) { std::cout << "A copy construction was made." << i_<<std::endl; } C& operator=( const C& other) { i_= other.i_ ; std::cout << "A copy assign was made."<< i_<<std::endl; return *this; } C( C&& other ) noexcept :i_( std::move(other.i_)) { std::cout << "A move construction was made." << i_ << std::endl; } C& operator=( C&& other ) noexcept { i_ = std::move(other.i_); std::cout << "A move assign was made." << i_ << std::endl; return *this; } }; 

AND

 auto vec2 = std::vector<C>{1,2,3,4,5}; cout << "reversing\n"; std::reverse(vec2.begin(),vec2.end()); 

With exit

 A copy construction was made.1 A copy construction was made.2 A copy construction was made.3 A copy construction was made.4 A copy construction was made.5 reversing A move construction was made.1 A move assign was made.5 A move assign was made.1 A move construction was made.2 A move assign was made.4 A move assign was made.2 

Now the opposite shows two two swaps (each using one move destination and two move constructs), but why can't temporary C objects created from the list of initializers be moved? I thought I had a list of integer initializers, but now I'm wondering if I have an intermediate list of Cs initializers that cannot be ported from (like its const). Is this the correct interpretation? - What's happening?

Live demo

+6
source share
3 answers

I thought I had a list of integer initializers, but now I'm wondering if I have an intermediate list of Cs initializers that cannot be ported from (like its const). Is this the correct interpretation?

It is right. vector<C> does not have an initializer_list<int> constructor or even an initializer_list<T> constructor for some template parameter T It has an initializer_list<C> constructor, which is created from all the ints you enter. Since initializer_list support is an array of constants, you get a bunch of copies, not a bunch of moves.

+9
source

As pointed out in my comment, you get copies because a vector like std::vector<C> expects std::initializer_list<C> , so your int list is created in a temporary list C , and this is a list C that is copied from.

One way you could get around this is to make a helper function. Using something like

 template <typename T, typename Y> std::vector<T> emplace_list(std::initializer_list<Y> list) { std::vector<T> temp; temp.reserve(list.size()); for (const auto& e: list) temp.emplace_back(e); return temp; } int main() { auto vec2 = emplace_list<C>({1,2,3,4,5}); } 

You can avoid creating copies of elements from the list, because you directly create them into a vector using emplace_back . If the compiler uses NRVO, then you don’t even have to move the vector from the function. See this live example for full g ++ output. Notice that I have a constructor seal, so you can see that it is called only.

+3
source

Thinking a little more. Here's the answer itself:

std::vector<C> does not have an initializer_list<int> constructor or a T event convertible to C. It has a constructor

 vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() ); 

So, the argument list initializer_list will be initializer_list<C> . The specified constructor can do nothing more than copy from the list of initializers, since they are immutable (used for expressing const, but the effect is the same here, it cannot move).

Oh, and this is also what NathanOliver wrote in a comment when I wrote this.

+1
source

Source: https://habr.com/ru/post/1014493/


All Articles