I'm new to C++0x and I'm trying to wrap my head around rvalue references and move constructors. I'm using g++ 4.4.6 with -std=c++0x, and I'm confused by the following piece of code:
class Foo
{
public:
Foo()
: p( new int(0) )
{
printf("default ctor\n");
}
Foo( int i )
: p( new int(i) )
{
printf("int ctor\n");
}
~Foo()
{
delete p;
printf("destructor\n");
}
Foo( const Foo& other )
: p( new int( other.value() ) )
{
printf("copy ctor\n");
}
Foo( Foo&& other )
: p( other.p )
{
printf("move ctor\n");
other.p = NULL;
}
int value() const
{
return *p;
}
private:
// make sure these don't get called by mistake
Foo& operator=( const Foo& );
Foo& operator=( Foo&& );
int* p;
};
Foo make_foo(int i)
{
// create two local objects and conditionally return one or the other
// to prevent RVO
Foo tmp1(i);
Foo tmp2(i);
// With std::move, it does indeed use the move constructor
// return i ? std::move(tmp1) : std::move(tmp2);
return i ? tmp1 : tmp2;
}
int main(void)
{
Foo f = make_foo( 3 );
printf("f.i is %d\n", f.value());
return 0;
}
I find that as written, the compiler uses the copy constructor build the object in main(). When I use the std::move line inside make_foo(), then the move constructor is used in main(). Why is std::move necessary inside make_foo()? I would think that although tmp1 and tmp2 are named objects inside make_foo(), when they're returned from a function they should become temporaries.
Best Answer
This is your problem:
A local variable in a function will only be moved from in the return statement if the return statement is just
return var;
. If you want to do that test you will need to use an if:The citation is a bit convoluted, but it is in 12.8/31 and 12.8/32
That is even if the expression is an lvalue, it will be considered to be an rvalue when the criteria in 12.8/31 is met, the second option in that block is:
Which determines that
return tmp;
allows for copy elision, butreturn (cond?tmp:tmp);
doesn't.Note that for the compiler to generate an implicit
std::move
in the return statement, the returned object must be a candidate for elision unless the it is also an argument to the function. Using the conditional operation inhibits copy elision, and at the same time inhibits the compiler from doing moving out of your objects. That second case might be simpler to code: