【问题标题】:Does returning a temporary object create a temporary object in C++?返回临时对象是否会在 C++ 中创建临时对象?
【发布时间】:2016-01-30 17:20:27
【问题描述】:

考虑以下 C++ 代码:

struct A {A(int);};
A foo() {return static_cast<A>(0);}
A x = foo();

这里static_cast&lt;A&gt;(0)按照标准[5.2.9-4]创建一个临时对象,它是一个prvalue。标准 [12.2-1] 说

类类型的临时对象在各种上下文中创建:将引用绑定到纯右值 (8.5.3)、返回纯右值 (6.6.3)、创建纯右值的转换 (4.1、 5.2.9, 5.2.11, 5.4),抛出异常 (15.1),进入处理程序 (15.3),以及在某些初始化中 (8.5)。

那么return语句会再次创建一个临时对象吗?

顺便问一下,谁能告诉我标准是否保证隐式类型转换会创建一个临时对象?

【问题讨论】:

标签: c++ return type-conversion temporary-objects


【解决方案1】:

(§4/6)提到

任何隐式转换的效果都与执行相应的声明和初始化,然后使用临时变量作为转换的结果相同。

所以是的,除非经过优化,否则应该创建一个临时文件,但我确信所有现代编译器都会在您的情况下执行复制省略。这种特殊的优化称为return value optimization (RVO)。您可以通过具有副作用的构造函数轻松测试它:

struct A {
    A(int){
        std::cout << "ctor";
    }
    A(const A & other)
    {
        std::cout << "copy ctor";
    }
    A(A&&other)
    {
        std::cout << "move ctor";
    }
};

【讨论】:

  • “但我确信所有现代编译器都会执行复制省略”——它们可以,但这并不一定意味着它们会。例如,GCC 有 -fno-elide-constructors 命令行选项来禁用复制省略。此外,复制省略是可选的,因为在一般情况下,在构造对象时可能不知道是否将其用作返回值。总会有标准允许复制省略的情况,但实现不会执行它。 (但对于 OP 的特定代码,如果任何现代编译器都无法应用 RVO,我也会感到惊讶。)
  • 构造函数的副作用不会导致RVO不执行,见my answer
  • 我知道复制省略,我只想知道没有复制省略的情况。我很抱歉忘记在问题中提及这一点。谢谢你的回答。
  • @m.s.:他就是这么说的。你可以和他们一起测试。
  • @m.s.,这就是重点,你可以通过看不到构造函数的输出来测试它。
【解决方案2】:

临时对象将(很可能)通过Return-Value-Optimization (RVO) 优化掉。

例子:

#include <iostream>
struct A
{
    A(int)
    {
        std::cout<< "A" << std::endl;
    }
    A(const A&)
    {
        std::cout << "A&" << std::endl;
    }
    A(A&&)
    {
        std::cout << "A&&" << std::endl;
    }
};
A foo() {return static_cast<A>(0);}

int main()
{
    A x = foo();
    return 0;
}

输出:live example

A

禁用 RVO 的输出:live example

A
A&&
A&&

【讨论】:

    【解决方案3】:

    简短回答:不,您的代码中只会创建一个 A

    为了实现这一点,编译器使用(Named) Return value optimization 消除了返回时不必要的对象创建/复制。更一般的情况,Copy elision,消除了不必要的对象复制,将用于大量相关情况。

    您可以使用GCC 选项-fno-elide-constructors 来查看差异。

    【讨论】:

      【解决方案4】:

      在这种特殊情况下的实际结果将取决于特定的编译器和优化级别。实际上,具有良好优化级别的体面的现代编译器可以完全删除任何临时对象。考虑一下:

      #include <iostream>
      
      using namespace std;
      
      struct A {
          A(int) { cout << __PRETTY_FUNCTION__ << endl; }
          ~A() { cout << __PRETTY_FUNCTION__ << endl; }
      };
      
      inline
      A foo() {
          return static_cast<A>(0);
      };
      
      
      int main(void) {
          A a = foo();
          cout << "hello world!" << endl;
      }
      

      带有 -O4 的 gcc-5.1.1 构建了一个可执行文件,它的字面意思是这样的:

      A::A(int)
      hello world!
      A::~A()
      

      【讨论】:

      • 甚至是-O3,因为-O4除此之外没有任何改变。 :)
      • 甚至没有任何-O 选项,因为复制省略不受这些选项的影响。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      相关资源
      最近更新 更多