为什么返回参数时不允许使用 RVO?

2024-01-01

[C++11: 12.8/31] 中有说明:

这种复制/移动操作的省略(称为复制省略)是允许的 [...] :

— 在具有类返回类型的函数的 return 语句中,当表达式是非易失性自动对象的名称时 (除了函数或 catch 子句参数之外) 与函数返回类型具有相同的 cv-unqualified 类型,可以通过直接将自动对象构造到函数的返回值中来省略复制/移动操作

这意味着

#include <iostream>

using namespace std;

struct X
{
    X() { }
    X(const X& other) { cout << "X(const X& other)" << endl; }
};

X no_rvo(X x) {
    cout << "no_rvo" << endl;
    return x;
}

int main() {
    X x_orig;
    X x_copy = no_rvo(x_orig);

    return 0;
}

将打印

X(const X& other)
no_rvo
X(const X& other)

为什么需要第二个复制构造函数?编译器不能简单地延长生命周期吗x?


Imagine no_rvo是在不同的文件中定义的main这样编译时main编译器只会看到声明

X no_rvo(X x);

并且不知道该类型的对象是否X已返回any与论证的关系。据当时所知,实施no_rvo也可以是

X no_rvo(X x) { X other; return other; }

所以当它例如编译该行

X const& x = no_rvo(X());

当最大限度地优化时,它将执行以下操作。

  • 生成要传递给的临时 Xno_rvo作为论据
  • call no_rvo,并将其返回值绑定到x
  • destruct它传递给的临时对象no_rvo.

现在如果返回值来自no_rvo与传递给它的对象是同一个对象,那么临时对象的销毁将意味着返回对象的销毁。但这是错误的,因为返回的对象绑定到一个引用,因此将其生命周期延长到该语句之外。然而,仅仅不破坏论证也不是解决方案,因为如果定义no_rvo是我上面展示的替代实现。因此,如果允许函数重用参数作为返回值,则可能会出现编译器无法确定正确行为的情况。

请注意,对于常见的实现,编译器无论如何都无法对其进行优化,因此,正式允许这样做并不是一个很大的损失。另请注意,编译器is如果可以证明这不会导致可观察行为的变化(所谓的假设规则),则无论如何都可以优化副本。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么返回参数时不允许使用 RVO? 的相关文章

随机推荐