请参阅下面的代码:
#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,那么性能可能会不好?
答案 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()
内指向的内存相同的内存。一旦指针超出范围,将被删除的内存。所以副本不仅是不可避免的,它绝对是至关重要的!