【问题标题】:Understanding return value optimization and returning temporaries - C++了解返回值优化和返回临时值 - C++
【发布时间】:2009-09-08 14:05:35
【问题描述】:

请考虑这三个功能。

std::string get_a_string()
{
    return "hello";
}

std::string get_a_string1()
{
    return std::string("hello");
}

std::string get_a_string2()
{
    std::string str("hello");
    return str;
}
  1. RVO 是否适用于所有三种情况?
  2. 可以像上面的代码那样返回一个临时的吗?我相信这没问题,因为我是按值返回它,而不是返回对它的任何引用。

有什么想法吗?

【问题讨论】:

    标签: c++ compiler-construction return-value-optimization


    【解决方案1】:

    在前两种情况下,将进行 RVO 优化。 RVO 是旧功能,大多数编译器都支持它。最后一种情况就是所谓的 NRVO(命名为 RVO)。这是 C++ 相对较新的特性。标准允许但不要求实现 NRVO(以及 RVO),但一些编译器支持它。

    您可以在 Scott Meyers 的书 More Effective C++. 35 New Ways to Improve Your Programs and Designs 的第 20 条中阅读有关 RVO 的更多信息。

    Here 是一篇关于 Visual C++ 2005 中 NRVO 的好文章。

    【讨论】:

    • VS2008有什么办法可以关闭包括RVO在内的所有优化?我已经关闭了所有,但它仍在做 RVO。这只是为了了解差异。
    • /Od 应该禁用 NRVO,但我不确定 RVO。
    • 我很确定 RVO 没有应用于调试版本
    • @Krugar:嗯,它被应用了。我正在运行调试版本。
    • MSVC 应用 RVO,即使优化被禁用。但是它并不适用 NRVO。
    【解决方案2】:

    首先,按值返回一个临时值是完全可以的,这就是你所做的。它被复制了,虽然原件会超出范围,但副本不会这样做,并且可以被调用者安全地使用。

    其次,所有三种情况实际上都是相同的(因为无论如何您都不会在第三种情况下访问临时),编译器甚至可能为所有这些情况发出相同的代码。因此,它可以在所有三种情况下使用 RVO。这完全取决于编译器。

    【讨论】:

      【解决方案3】:

      所有情况都是正确的。它们都将构造一个临时的并应用返回类型的复制构造函数。必然地,如果没有复制构造函数,代码就会失败。

      在大多数编译器下,RVO 将发生在所有三种情况下。唯一的区别是标准不强制执行的最后一个。这是因为您有一个命名变量。但是大多数编译器都足够聪明,仍然可以将 RVO 应用于它……命名变量声明得越晚,应用的转换越少,RVO 应用于命名变量的几率就越大。

      顺便说一句,返回引用当然是可能的,正如您在其他代码中看到的那样。你不能做的是返回一个本地对象的引用。

      std::string& get_a_string2()
      {
          std::string str("hello");
          return str; //error!
      }
      

      如您所知,会产生编译时错误。然而,

      std::string& get_a_string2(std::string& str)
      {
          // do something to str
          return str; //OK
      }
      

      会工作得很好。在这种情况下,不涉及构造或复制构造。只需函数返回对其参数的引用。

      【讨论】:

      • 但是,我更希望将该函数设为 void,并且在处理参数时不返回任何内容。返回引用参数只会让其他人在看代码时感到困惑。
      【解决方案4】:
      1. 这取决于您的编译器 - 您指的是什么平台?找出答案的最佳方法是编译一个非常小测试应用程序并检查编译器生成的 ASM。

      2. 是的,没关系,尽管你从不提及你关心的事情;速度?风格?您可以将本地临时对象添加到 const 引用 - 临时对象的生命周期将延长到引用的生命周期 - 试试看吧! (Herb Sutter 对此进行了解释 here)例如,请参见帖子末尾。

      IMO 相信您的编译器会为您优化代码几乎总是更好。在极少数情况下您需要关心这类事情(非常低级的代码就是这样一个领域,您与硬件寄存器交互)。

      int foo() { return 42; }
      
      int main(int, char**)
      {
          const int &iRef = foo();
          // iRef is valid at this point too!
      }
      

      【讨论】:

      • “你可以返回一个对本地临时的 const 引用”不,那是错误的。您不能返回对该函数中创建的本地或临时对象的引用。一旦函数返回,它们将被销毁,并且引用返回值将引用垃圾。我想你想到了const T& t = T();,这是另一回事。
      • 是的,这就是我的想法。我现在说得更清楚了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      • 2023-03-30
      • 1970-01-01
      • 2020-06-09
      • 2019-04-01
      • 2019-01-21
      相关资源
      最近更新 更多