函数返回的 RVO 和 rvalue 如何工作

How does RVO and rvalue returned by the function work?

本文关键字:何工作 工作 rvalue 返回 RVO 函数      更新时间:2023-10-16

为了理解编译器如何选择类的构造函数,我编写了以下代码:

#include <iostream>
struct Widget
{
    Widget(Widget&& w){std::cout << "Move ctor" << std::endl;}
    Widget(void){std::cout << "Default ctor" << std::endl;}
    Widget(const Widget& w){std::cout << "Copy ctor" << std::endl;}
};
Widget make_widget(void) //helper function
{
    Widget w;
    return w;
}
int main(void)
{
    Widget w(make_widget());
}

根据有效现代C++的第 25 项,由于返回值优化,编译器将 w 视为右值引用。所以我期望Widget w(make_widget())调用移动构造函数。但事实并非如此。进一步移动,它只打印

Default

所以我不知道调用了哪个版本的构造函数。然后我也尝试显式返回 rvalue。也就是说,return std::move(w).考虑到上述结果,我的期望与我相反,它正确地调用了移动构造函数,并打印了

Default
Move

看来我处于价值的迷宫中。请告诉我那里发生了什么。

根据有效现代C++的第 25 项,由于返回值优化,编译器将w视为右值引用。

不,我敢肯定作者不是那个意思。 RVO 与是否为右值引用无关。make_widgetmain 中的wWidget 类型的左值。 如果你有一个类型 Widget&& 的变量,它的值类别是左值(因为它有一个恒等式),它的类型是右值引用。

您看到的Default是由于make_widget()内部Widget w;。 由于返回值优化,避免了将其复制到main中的w的操作,因此您看不到更多内容。如果您想在没有 RVO 的情况下查看输出,请在使用它时将-fno-elide-constructors传递给g++(无法使用 VC++ 禁用 RVO),您将看到

Default (first creation)
Move    (creation of temporary with the move ctor)
Move    (copying of temporary to the one in main)

当您将return w更改为 return std::move(w) 时,为返回创建的临时是用右值构造的,因此您会在输出中看到一个额外的 Move