return std::move 一个 class 和一个 unique_ptr 成员

return std::move a class with a unique_ptr member

为什么我不能 return 包含 std::unique_ptr 的 class,使用 std::move 语义(我认为),如下例所示?我认为 return 会调用 class A 的移动构造函数,这会 std::move std::unique_ptr。 (我使用的是 gcc 11.2,C++20)

示例:

#include <memory>

class A {
  public:
    explicit A(std::unique_ptr<int> m): m_(std::move(m)) {}
  private:
    std::unique_ptr<int> m_;
};

A makit(int num) {
    auto m = std::make_unique<int>(num);
    return std::move(A(m));  // error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'x86-64 gcc 11.2 #1
}

int main() {
    auto a = makit(42);
    return 0;
}

我认为解决方案是 return 和 std::unique_ptr,但在我屈服之前,我想知道为什么移动方法不起作用。

I thought that the return would invoke the move ctor of class A, which would std::move the std::unique_ptr.

全部正确,但移动构造函数仅移动 A 的成员。它不能为您移动不相关的卫星唯一指针。

在表达式 A(m) 中,您使用 m 作为左值。这将尝试复制 m 以初始化 A::A 的参数 m(顺便说一句,糟糕的命名方案来解释所有这些)。如果移动 that,即 A(std::move(m)),表达式将变得合式。

关于那个主题,std::move(A(...)) 中的外部 std::move 是多余的。 A(...) 已经是 A 类型的右值。额外的 std::move 在这里没有任何用处。

  • 简而言之,你不应该 return A 使用 std::move 因为编译器会为你做优化工作(通过一个叫做 RVO 的技巧:What are copy elision and return value optimization?).

  • 另一点是 m_(std::move(m)) 中的 std::move 完全没有必要,因为无论使用 std::move 或不是。请记住,std::move 不能保证移动操作并且不会阻止在所有情况下进行处理。

总之,您最好使用 return A( std::move(m) ); 而不是 return std::move(A(m));。在您的 return 声明中, A(m) 已经是 prvalue 并且您不需要将其转换为 xvalue 使用 std::move 以便 return 有效。只需 return 它的 编译器就会为你完成这个技巧。