我是C ++ 0x的新手,我正试图围绕rvalue引用并移动构造函数。我正在使用带有-std = c ++ 0x的g ++ 4.4.6,我对以下代码感到困惑:
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;
}
我发现编写时,编译器使用复制构造函数在main()中构建对象。当我在make_foo()中使用std :: move行时,在main()中使用了移动构造函数。为什么在make_foo()中需要std :: move?我认为虽然tmp1和tmp2是make_foo()中的命名对象,但当它们从函数返回时,它们应该成为临时对象。
答案 0 :(得分:11)
这是你的问题:
return i ? tmp1 : tmp2;
如果return语句只是return var;
,函数中的局部变量只会从return语句中移出。如果您想进行该测试,则需要使用if:
if (i) {
return tmp1;
} else {
return tmp2;
}
引用有点复杂,但是在12.8 / 31和12.8 / 32
12.8 / 32当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策选择首先执行副本的构造函数,就像对象由rvalue [...]
指定一样
即使表达式是左值,当满足12.8 / 31中的条件时,它将被视为右值,该块中的第二个选项是:
12.8 / 31在具有类返回类型的函数的return语句中,当表达式是具有相同cv-unqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时作为函数返回类型,可以通过将自动对象直接构造为函数的返回值来省略复制/移动操作。
确定return tmp;
允许复制省略,但return (cond?tmp:tmp);
不允许。
请注意,对于编译器在return语句中生成隐式std::move
,返回的对象必须是elision的候选对象,除非它也是函数的参数。使用条件操作会禁止复制省略,同时禁止编译器移出对象。第二种情况可能更容易编码:
Foo make_foo(Foo src) {
return src; // Copy cannot be elided as 'src' is an argument
}