为什么在return语句中使用move构造函数是合法的?

时间:2014-10-06 17:55:16

标签: c++ c++11

请考虑以下事项:

#include <iostream>

#define trace(name) std::cout << #name << " (" << this << "), i = " << i << std::endl

class C
{
    C(C const&);
    C& operator=(C const&);
public:
    int i;
    C() : i(42) { trace(CTOR); }
    C(C&& other) : i(other.i) { trace(MOVE); other.i = 0; }
    C& operator=(C&& other) { trace(ASGN); other.i = 0; return *this; }
    ~C() { trace(DTOR); }
};

C
func1(bool c)
{
    C local;
    if (c)
        return local;
    else
        return C();
}

int
main()
{
    C local(func1(true));
    return 0;
}

MSC和g ++都允许return local,并在执行此操作时使用移动构造函数(如输出所示)。虽然这对我来说很有意义,而且我认为可能应该是这样,但我找不到授权它的标准中的文本。据我所知,移动构造函数的参数必须是prvalue(它显然不是)或xvalue;它实际上是一个左值,它会使返回与函数体中的C other = local;一样非法(它无法编译)。

2 个答案:

答案 0 :(得分:6)

当在C ++ 11中添加到C ++的移动语义时,决定在哪里自动移动构造。

遵循的一般规则是,当复制省略合法时,应该发生隐式移动。

当您有一个要复制到另一个实例的匿名对象时,复制elision是合法的。编译器可以合法地删除副本,并将这两个对象视为一个,其生命周期等于两个对象生命周期的并集。

复制省略合法的另一个时间是从函数返回局部变量。这被称为NRVO(命名为返回值优化)。 C ++ 03中的这种优化允许您声明一个返回值,它直接在返回值所在的位置构造。当您return retval;时,不会发生任何副本。

当你在函数的不同位置返回多个不同的对象时,基本上不可能没有内联。

但是,当添加移动语义时,这个地方是可能发生隐式移动的另一个地方。返回与return local_var;相同类型的变量的函数中的local_var可以省略,失败,您可以隐式地从local_var移动到返回值

答案 1 :(得分:4)

  

C ++ 11 12.8 / 32:当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由< em> lvalue ,首先执行选择复制构造函数的重载决策,好像该对象是由 rvalue 指定的。

返回本地自动变量符合elision标准;将它视为 rvalue 就像选择移动构造函数一样。