在shared_ptr中返回对象时,RVO会工作吗?

时间:2014-04-28 08:00:55

标签: c++ memory c++11 return shared-ptr

请参阅下面的代码:

#include <memory>

struct A {/*...*/};

void goo(std::shared_ptr & p) {
  p = std::shared_ptr<A>(new A);
}

A foo() {
  std::shared_ptr<A> ptr;
  goo(ptr);
  return *ptr;
}

int main(int argc, char *argv[])
{
  auto r = foo();
  return 0;
}

我对foo函数的返回值感到困惑,编译器会在这里复制A-object吗?如果是这样,在这种情况下会忽略RVO,那么性能可能会不好?

3 个答案:

答案 0 :(得分:4)

正式两个操作。 *ptr返回的引用到临时返回值的副本以及r中从临时目标到最终目标的移动。

第一个副本无法优化 1 ,因为无法让编译器控制指针引用的内存。它根本无法使共享指针指向它想要构造返回值的任何地方,它也不能使用指针的内存,因为它不能假设任何关于它的生命周期。

第二个复制/移动可以进行优化,因为在{§12.8/ 31中该条款允许的情况下,可以直接在r中构建return temporary:

  
      
  • 当复制/移动尚未绑定到引用(12.2)的临时类对象时   对于具有相同cv-unqualified类型的类对象,可以省略复制/移动操作   将临时对象直接构造到省略的copy / move
  • 的目标中   

1 您可以使用std::move明确表示移动。因为std::shared_ptr::operator*返回 l 值引用,编译器将默认推导出副本。

答案 1 :(得分:1)

使用RVO和内联方法,编译器可以将您的代码优化为类似

struct A {/*...*/};

int main(int argc, char *argv[])
{
  std::shared_ptr<A> ptr;
  goo(ptr);
  A r {*ptr};
  return 0;
}

所以A被复制一次

答案 2 :(得分:1)

C ++ 11标准在§12.8/ 31中解释了RVO的条件:

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

请注意,它表示“非易失性自动对象的名称”。由于*ptr不是名称,编译器无法复制/移动椭圆。

编辑:

进一步思考:

如果这里根本没有制作副本,r中的变量main()会以某种方式神奇地使用与ptr foo()内指向的内存相同的内存。一旦指针超出范围,将被删除的内存。所以副本不仅是不可避免的,它绝对是至关重要的!