I just learned about guaranteed copy elision in C++17. According to the answer on that question:
When you do
return T();
, this initializes the return value of the function via aprvalue
. Since that function returns T, no temporary is created; the initialization of theprvalue
simply directly initializes the return value.The thing to understand is that, since the return value is a
prvalue
, it is not an object yet. It is merely an initializer for an object, just likeT()
is.
So I was wondering, does this work for anything other than:
T f() {return T();}
T t = f();
So I wrote this code with emplace_back
to test it:
#include <vector>
#include <iostream>
struct BigObj{
BigObj() = default;
BigObj(int) { std::cout << "int ctor called" << std::endl; }
BigObj(const BigObj&){
std::cout << "copy ctor called" << std::endl;
}
BigObj(BigObj&&){
std::cout << "move ctor called" << std::endl;
}
};
BigObj f(){ return BigObj(2); }
int g(){ return 2; }
int main(){
std::vector<BigObj> v;
v.reserve(10);
std::cout << "emplace_back with rvalue \n";
v.emplace_back(1+1);
std::cout << "emplace_back with f()\n";
v.emplace_back(f());
std::cout << "emplace_back with g()\n";
v.emplace_back(g());
}
This is the output I get (with copy elision disabled):
emplace_back with rvalue
int ctor called
emplace_back with f()
int ctor called
move ctor called
emplace_back with g()
int ctor called
It seems that the move constructor is still called even though a prvalue
is passed directly into emplace_back
, which I thought could be used to directly construct an object instead of being used to construct a temporary and then moving it.
Is there a more elegant way to avoid the move constructor call with emplace_back
other than doing something like what the g()
function does?