14

我知道当按值将对象传递给函数时,如果没有复制省略,则始终调用移动构造函数。按值返回对象呢?

例如,假设我们有一个Foo具有移动构造函数的类,并且我们有一个返回Foo对象的函数。

Foo g() {
    Foo f;

    // do something with f

    return f;
}

如果我们假设没有 RVO,是否保证调用移动构造函数?

更新:我想我没有清楚地表明我的意图。我只是想知道在最坏的情况下我可以让对象移动而不是复制。RVO 或 NRVO 发生,我很高兴。而且我还应该说移动构造函数和移动赋值没有被删除并且被正确实现。

4

3 回答 3

8

是的。参见 [class.copy] p32

当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定之外,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。如果重载决议失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。[注:无论是否会发生复制删除,都必须执行此两阶段重载解决方案。它确定如果不执行省略则要调用的构造函数,并且即使调用被省略,所选构造函数也必须是可访问的。——尾注]

于 2012-06-18T18:06:16.957 回答
2

在这种情况下,由于返回值具有名称 ( f),因此将应用 NRVO(命名返回值优化)。

因此,仅基于措辞的技术答案是,缺少 RVO 不会阻止复制省略,因为 NRVO 仍然可以允许它。

过去,我相信返回值的移动/复制之间的选择可以/将取决于 Foo 的定义 - 肯定有时间它会被复制而不是移动,例如如果您明确删除了移动构造函数和移动赋值运算符,或者你没有定义移动构造/赋值,它不适合隐式合成它们。

编辑:[回应编辑的问题]:拥有移动构造函数仍然不能保证结果会被移动。一个明显的例子是,如果您删除了移动赋值运算符,并且正在分配结果(而不是使用它来初始化)。在这种情况下,删除的移动赋值运算符将阻止移动返回值。

但是,要回答您可能遇到的问题,一般规则是,如果可能,将进行移动,并且当且仅当某些东西阻止结果被移动时,它才会回退到复制。

于 2012-06-18T17:47:25.127 回答
2

规则是,只要允许但不发生复制省略,则将使用移动构造函数(如果可用),否则将使用复制构造函数。

确切的行为由以下定义[class.copy]/32

当满足或将满足复制操作的省略标准时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。如果重载决议失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是 cv 限定的),则再次执行重载决议,将对象视为左值。

于 2012-06-18T17:52:18.970 回答