关于以下示例我几乎没有问题:
unique_ptr<A> foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<A>&& foo(){
unique_ptr<A> a = make_unique<A>(5);
return move(a);
}
unique_ptr<int> foo(){
unique_ptr<int> a = make_unique<int>(5);
return a;
}
第一个例子:
为什么编译器允许这样的事情?我们不是要返回右值参考吗?为什么编译器允许这样的隐式转换?谁持有基础对象?谁负责在最后摧毁它?
第二个例子:
当我这样做时,我从函数的返回类型中获取垃圾。虽然我认为这是“正确”的方法,但是我们不是在宣布我们正在返回一个rval引用,并且实际上是在移动我们的对象吗?
第三个例子:
如果删除unique_ptr
的复制构造函数,这里会发生什么允许这个函数? a
是unique_ptr
,我们按值返回,所以不会创建副本吗?
答案 0 :(得分:3)
这是一个快速的破败:
第一个例子很好,但std::move(a)
禁止cooy-elision。当然,移动比复制更好,但没有工作,但更好。
右值引用仍然是引用,您需要保持引用的对象处于活动状态。在可以访问返回的引用之前,将销毁引用的本地对象。返回右值引用很少有用,尽管某些标准函数会这样做(std::move(x)
,std::forward<T>(x)
,std::declval<T>()
)但是对于这些函数,返回的是非本地对象。
当可以进行复制省略时,会隐式移动对象。这是复制省略可行的方法。
似乎你不知道 copy elision :在需要拷贝的某些情况下,即使程序的语义发生了变化,编译器也可以忽略副本(即副作用)副本ctor和dtor没有发生)。实际上,复制省略允许将对象直接构建在正确的位置。只有少数情况允许复制省略(确切的规则更复杂):
由于在这些情况下没有复制,因为对象已经在正确的位置,因此扩展规则以移动操作似乎是合理的:当允许复制省略时,隐式移动对象。实际效果是,复制或移动构造函数就足够了。任何体面的编译器既不会复制也不会移动,而是复制/移动构造。