Consider the following code:
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
A(int) { cout << "int" << endl; }
A(A&&) { cout << "move" << endl; }
A(const A&) { cout << "copy" << endl; }
};
int main()
{
vector<A> v
{
A(10), A(20), A(30)
};
_getch();
return 0;
}
The output is:
int
int
int
copy
copy
copy
A(10)
, A(20)
and A(30)
are temporaries, right?
So why is the copy constructor called? Shouldn't the move constructor be called instead?
Passing move(A(10))
, move(A(20))
, move(A(30))
instead, the output is:
int
move
int
move
int
move
copy
copy
copy
In this case either copy or move constructor is called.
What's happening?
std::vector
can be constructed from astd::initializer_list
, and you are calling that constructor. The rules for initializer_list construction state that this constructor is aggressively preferred:Also, because of the sort of weird implementation of an
initializer_list
as an array allocated under the hood, elements of the corresponding array that thestd::initializer_list<E>
refers to are forced to be copy initialized (which can be elided):(Both references above from N3337 [dcl.init.list])
However, in your first example the copies can/are elided despite the name ([dcl.init]/14) so you don't see an extra copy construction (they can also be moved) You can thank your compiler for that, because copy elision is not required in C++11 (although it is in C++17).
See [class.copy] for more details ("When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object...").
The final part is key:
[support.initlist] states that
This means that the
std::vector
cannot take over the memory directly; it must be copied, this is where you ultimately see the copy constructions being called.In the second example it is as Kerrek SB stated, you prevented the copy-elision I mentioned earlier and caused an additional overhead of a move.