return 通过移动后的右值引用

return by rvalue reference after move

我对以下示例有几个问题:

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;
}

第一个例子:

为什么编译器允许这样的事情?我们不是 return 右值引用吗?为什么编译器允许这样的隐式转换?谁持有底层对象?最后谁负责销毁它?

第二个例子:

当我这样做时,我从 return 类型的函数中得到了垃圾。虽然我认为这是执行此操作的 "right" 方法,但我们不是在声明我们正在 return 一个 rval 引用,并且实际上是这样移动我们的对象吗?

第三个例子:

如果删除了unique_ptr的拷贝构造函数,这里会发生什么情况允许这个函数? a 是一个 unique_ptr,我们按值 return 计算它,所以不会创建一个副本吗?

这是一个快速 运行-down:

  1. 第一个例子还可以,但是 std::move(a) 抑制了 cooy-elision。当然,移动比复制更好,但没有工作更好。

  2. 右值引用仍然是引用,您需要保持引用对象的活动。在可以访问 returned 引用之前,引用的本地对象被销毁。 return 右值引用很少有用,尽管一些标准函数这样做(std::move(x)std::forward<T>(x)std::declval<T>())但是对于这些有一个非本地对象 returned.

  3. 当复制省略成为可能时,对象被隐式移动。这就是复制省略仍然可行的方法。

您似乎没有意识到复制省略:在某些需要复制的情况下,即使程序的语义发生了变化,编译器也可以省略复制(即,不会发生复制 ctor 和 dtor 的副作用)。实际上,复制省略允许立即在正确的位置构造对象。只有少数情况允许复制省略(具体规则更复杂):

  1. 复制临时对象时。
  2. 当 returning 局部变量时。
  3. 抛出局部变量时。
  4. 重新抛出捕获的对象时。

由于在这些情况下没有进行任何复制,因为对象已经在正确的位置,因此将规则扩展到移动操作似乎是合理的:当允许复制省略时,对象被隐式移动。最终效果是拥有复制或移动构造函数就足够了。任何像样的编译器既不会复制也不会移动,但会省略 copy/move 结构。