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::vectorcan 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_listas 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::vectorcannot 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.