To demo move semantics, I wrote the following example code, 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 output
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 reverse shows the 2 two swaps (each using one move assign and two move constructs), but why are the temporary C
objects created from the initializer list not possible to move from? I thought I had an initializer list of integers, but I'm now wondering if what I have in between is an initializer list of Cs, which can't be moved from (as its const). Is this a correct interpretation? - What's going on?
Thinking a bit more. Here's the self answer:
std::vector<C>
does not have a constructor ofinitializer_list<int>
, or event of T convertible to C. It does have the constructorSo, the
initializer_list
argument list will be aninitializer_list<C>
. Said constructor can do nothing else than copy from the initializer list since they are immutable (used to say const, but the effect here is the same, it can't be move from).Oh, and that's also what NathanOliver wrote in a comment as I wrote this.
As detailed in my comment you get the copies because a vector of type
std::vector<C>
expects anstd::initializer_list<C>
so your list ofint
's is constructed into a temporary list ofC
's and it is that list ofC
's that is being copied from.One way you could get around this though is to make a helper function. Using something like
You can avoid making copies of the elements from the list because you directly construct them into the vector using
emplace_back
. If the compiler applies NRVO then you do not even have a move of the vector out of the function. See this live example for g++'s full output. Do note I have the constructor printing just so you can see it is the only one called.This is correct.
vector<C>
does not have aninitializer_list<int>
constructor or even aninitializer_list<T>
constructor for some template parameterT
. What it does have is aninitializer_list<C>
constructor - which is built up from all the ints you pass in. Since the backing ofinitializer_list
is a const array, you get a bunch of copies instead of a bunch of moves.