为什么在返回参数时不允许RVO
Why is RVO disallowed when returning a parameter?
如[C++11:12.8/31]所述:
这种复制/移动操作的省略,称为复制省略,是允许的[…]:
--在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型具有相同cv不合格类型的非易失性自动对象(而不是函数或catch子句参数)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作
这意味着
#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的寿命吗?
假设no_rvo
定义在与main
不同的文件中,因此在编译main
时,编译器将只看到声明
X no_rvo(X x);
并且不知道返回的类型为X
的对象是否与参数具有任何关系。据当时所知,no_rvo
的实现也可以是
X no_rvo(X x) { X other; return other; }
因此,当它例如编译线路时
X const& x = no_rvo(X());
当最大限度地优化时,它将执行以下操作。
- 生成要作为参数传递给
no_rvo
的临时X - 调用
no_rvo
,并将其返回值绑定到x
- destruct它传递给
no_rvo
的临时对象
现在,如果no_rvo
的返回值与传递给它的对象是同一个对象,那么销毁临时对象就意味着销毁返回的对象。但这是错误的,因为返回的对象绑定到引用,因此将其生存期延长到该语句之后。然而,简单地不破坏参数也不是解决方案,因为如果no_rvo
的定义是我上面展示的替代实现,那就错了。因此,如果允许函数重用参数作为返回值,则可能会出现编译器无法确定正确行为的情况。
请注意,对于常见的实现,编译器无论如何都无法对其进行优化,因此这并不是一个很大的损失,因此它不被正式允许。还要注意的是,如果编译器能够证明这不会导致可观察行为的变化(所谓的"好像规则"),那么它就可以优化拷贝。
RVO的常见实现是调用代码传递内存块的地址,函数应在该地址构造其结果对象。
当函数结果直接是一个不是形式参数的自动变量时,该局部变量可以简单地放置在调用方提供的内存块中,然后返回语句根本不进行复制。
对于通过值传递的参数,调用机器代码必须将其实际参数复制到形式参数中;s的位置,然后再跳转到函数。为了让函数将结果放在那里,它必须首先销毁形式参数对象,这有一些棘手的特殊情况(例如,当该构造直接或间接引用形式参数对象时)。因此,这里的优化在逻辑上必须为函数结果使用一个单独的调用提供的内存块,而不是用正式的参数位置来标识结果位置。
但是,未在寄存器中传递的函数结果通常由调用者提供。也就是说,对于表示正式论点的return
表达式,人们可以合理地将其称为RVO,一种减少的RVO,这是无论如何都会发生的事情。而且它不符合文本“通过将自动对象直接构造为函数的返回值";。
总之,要求调用方传入值的数据流意味着初始化正式参数存储的必然是调用方,而不是函数。因此,从形式参数复制回通常是不可避免的(这个狡猾的术语涵盖了编译器可以做非常特殊的事情的特殊情况,特别是对于内联机器代码)。然而,它是初始化任何其他本地自动对象的函数&rsquo的;的存储空间,然后;做RVO没有问题。
- 为什么 Clang 不允许"and"作为函数名称?
- 不允许在向量中添加更多元素
- std::带有自定义缓冲区的 iostream 不允许我写入
- Visual Studio 2017 不允许我创建 C++ 专用模板
- 返回时不允许隐式转换
- 为什么 c++(g++) 不允许模板返回类型和函数名称之间有空格?
- 为什么 c++ 不允许(自动)强制转换?
- 为什么 c++11 不允许使用自动
- 为什么不允许成员函数和非成员函数之间的函数重载?
- 为什么不允许使用可变长度数组作为向量元素?
- 余数除法和不允许除以零 (c++) 时遇到问题
- C++从外部类继承的嵌套类;不允许使用不完整的类型
- 在 c++ 中三元运算符中不允许继续(关键字)吗?
- 为什么在指向对象的迭代器上调用函数不允许我更改对象本身?
- 错误:在第 6 行'{'标记之前,此处不允许使用函数定义
- 不允许在类定义之外重写
- 不允许运算符 const 参数调用 const 成员函数
- E0322:不允许使用抽象类类型 " " 的对象
- error dllimport 函数的定义不允许在一个特定的联合中,而其他类、结构和联合将按预期导出
- 为什么在返回参数时不允许RVO