I'm using MSVC, Visual Studio 2013.
Suppose I have a struct:
struct my_pair {
int foo, bar;
};
And I want to add a bunch of these efficiently, without too creating a temporary and then discarding it:
vector<my_pair> v;
v.push_back(41, 42); // does not work [a]
v.push_back({41,42}); // works [b]
v.emplace_back(41,42); // does not work [c]
v.emplace_back({41,42}); // does not work [d]
v.emplace_back(my_pair{41,42}); //works [e]
Now if I add a constructor and copy constructor to my code:
my_pair(int foo_, int bar_) : foo(foo_), bar(bar_)
{
cout << "in cstor" << endl;
}
my_pair(const my_pair& copy) : foo(copy.foo), bar(copy.bar)
{
cout << "in copy cstor" << endl;
}
Then the behavior changes:
v.push_back(41, 42); // does not work [f]
v.push_back({41,42}); // displays "in cstor" and "in copy cstor" [g]
v.emplace_back(41,42); // displays "in cstor" [h]
v.emplace_back({41,42}); // does not work [i]
v.emplace_back(my_pair{41,42}); // "in cstor" and "in copy cstor" [j]
If I add a move constructor:
my_pair(my_pair&& move_) : foo(move_.foo), bar(move_.bar)
{
cout << "in move cstor" << endl;
}
Then:
v.emplace_back(my_pair{41,42}); //displays "in cstor", "in move cstor" [k]
v.emplace_back({41,42}); // still does not work [l]
v.push_back({41,42}); // displays "in cstor", "in move cstor" [m]
Questions:
for [a,b] I understand the reason for working and not working.
for [c], it doesn't work because there is no constructor to forward the arguments to.
for [d], why doesn't this work like in the push case?
for [e], why does it work when the class name is added?
for [h], it seems like this is the most efficient code if there is a constructor that maps the arguments to the members
for [j], it seems like this is as bad as a push_back and with extra typing I'm not sure why anyone should do this over push_back
for [k,m], with the addition of a move constructor it seems like push_back(T&&)
is being called which results in the same performance as emplace. But again, with the extra typing I'm not sure why anyone would do this.
I read that MSVC doesn't add a move constructor for you: Why is copy constructor called in call to std::vector::emplace_back()?
What is the difference between [d,e] and why is emplace picky about it. And why does push_back(T&&)
work without addition of the struct's name?
I can only get the full benefits of emplace if I know that there is a constructor that takes each member as argument?
Should I just stick with push_back
? Is there any reason to use emplace_back(structname{1,2,3})
instead of push_back({1,2,3})
because it will end up calling push_back(T&&)
anyway, and is easier to type?
Third, how does emplace_back(arg1,arg2,etc)
, do its magic to avoid the copy or move constructor completely?
emplace_back({42, 42}
generally doesn't work because that becomes aninitializer-list
of two integers, rather than amy_pair
struct. I think the emplace_back call might work if you also added aninitializer list
constructor. Of course, one would think this would work out directly, but you also have to consider the perfect-forwarding semantics ofemplace_back
: in most cases, unless you explicitly name the type, perfect-forwarding functions likeemplace_back
will take whatever is given exactly at face value with no extra considerations or conversions. โ user3010322