我是c++ 11中移动语义的新手,我不太清楚如何在构造函数或函数中处理unique_ptr参数。考虑这个类引用自己:

#include <memory>

class Base
{
  public:

    typedef unique_ptr<Base> UPtr;

    Base(){}
    Base(Base::UPtr n):next(std::move(n)){}

    virtual ~Base(){}

    void setNext(Base::UPtr n)
    {
      next = std::move(n);
    }

  protected :

    Base::UPtr next;

};

这是我应该如何写函数采取unique_ptr参数?

我需要在调用代码中使用std::move吗?

Base::UPtr b1;
Base::UPtr b2(new Base());

b1->setNext(b2); //should I write b1->setNext(std::move(b2)); instead?

当前回答

tl;dr:不要像那样使用unique_ptr。

我相信您正在制造一个可怕的混乱——对于那些需要阅读您的代码、维护它以及可能需要使用它的人来说。

如果有公开的unique_ptr成员,则只接受unique_ptr构造函数参数。

Unique_ptr的所有权和生命周期管理的包装原始指针。它们非常适合本地化使用——但对于接口来说并不好,实际上也不是有意的。想接口吗?记录你的新类的所有权,并让它获得原始资源;或者,在指针的情况下,使用核心指南中建议的owner<T*>。

只有当你的类的目的是保存unique_ptr,并让其他人同样使用这些unique_ptr时,你的构造函数或方法才有理由使用它们。

不要暴露您在内部使用unique_ptrs的事实。

Using unique_ptr for list nodes is very much an implementation detail. Actually, even the fact that you're letting users of your list-like mechanism just use the bare list node directly - constructing it themselves and giving it to you - is not a good idea IMHO. I should not need to form a new list-node-which-is-also-a-list to add something to your list - I should just pass the payload - by value, by const lvalue ref and/or by rvalue ref. Then you deal with it. And for splicing lists - again, value, const lvalue and/or rvalue.

其他回答

编辑:这个答案是错误的,尽管严格来说,代码是可以工作的。我把它留在这里只是因为下面的讨论太有用了。另一个答案是我上次编辑这篇文章时给出的最佳答案:如何将unique_ptr参数传递给构造函数或函数?

::std::move的基本思想是,传递unique_ptr的人应该使用它来表示他们知道他们传递的unique_ptr将失去所有权。

这意味着您应该在方法中使用unique_ptr的右值引用,而不是unique_ptr本身。这无论如何都不起作用,因为传入普通的unique_ptr将需要创建一个副本,而这在unique_ptr接口中是显式禁止的。有趣的是,使用一个命名的右值引用将它再次变成一个左值,所以你需要在你的方法中使用::std::move。

这意味着你的两个方法应该是这样的:

Base(Base::UPtr &&n) : next(::std::move(n)) {} // Spaces for readability

void setNext(Base::UPtr &&n) { next = ::std::move(n); }

然后使用这些方法的人会这样做:

Base::UPtr objptr{ new Base; }
Base::UPtr objptr2{ new Base; }
Base fred(::std::move(objptr)); // objptr now loses ownership
fred.setNext(::std::move(objptr2)); // objptr2 now loses ownership

如您所见,::std::move表示指针将在最相关和最有帮助的位置失去所有权。如果这种情况发生在不可见的情况下,使用您的类的人会非常困惑,因为没有明显的原因,objptr突然失去了所有权。

Base(Base::UPtr n):next(std::move(n)) {}

应该会好很多吧

Base(Base::UPtr&& n):next(std::forward<Base::UPtr>(n)) {}

and

void setNext(Base::UPtr n)

应该是

void setNext(Base::UPtr&& n)

同样的身体。

和…handle()中的evt是什么??

是的,如果你在构造函数中按值获取unique_ptr,你就必须这样做。明确是件好事。由于unique_ptr是不可复制的(私有复制ctor),您所写的应该会给您一个编译器错误。

投票最多的答案。我更喜欢通过右值引用传递。

我理解传递右值引用可能导致的问题。但是让我们把这个问题一分为二:

调用者:

我必须写代码基准newBase(std::move(<左值>))或基准newBase(<右值>)。

被:

标准库作者应该保证,如果它想拥有所有权,它会实际移动unique_ptr成员来初始化。

这是所有。

如果传递右值引用,它将只调用一个“move”指令,但如果传递值,它将调用两个。

是的,如果库作者不是这方面的专家,他可能不会移动unique_ptr来初始化成员,但这是作者的问题,而不是你。无论它是通过值还是右值引用传递,你的代码都是一样的!

如果您正在编写一个库,现在您知道应该保证它,那么就这样做吧,传递右值引用比传递值是更好的选择。客户端谁使用你的库将只是编写相同的代码。

现在,回答你的问题。如何将unique_ptr参数传递给构造函数或函数?

你知道什么是最好的选择。

http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html

tl;dr:不要像那样使用unique_ptr。

我相信您正在制造一个可怕的混乱——对于那些需要阅读您的代码、维护它以及可能需要使用它的人来说。

如果有公开的unique_ptr成员,则只接受unique_ptr构造函数参数。

Unique_ptr的所有权和生命周期管理的包装原始指针。它们非常适合本地化使用——但对于接口来说并不好,实际上也不是有意的。想接口吗?记录你的新类的所有权,并让它获得原始资源;或者,在指针的情况下,使用核心指南中建议的owner<T*>。

只有当你的类的目的是保存unique_ptr,并让其他人同样使用这些unique_ptr时,你的构造函数或方法才有理由使用它们。

不要暴露您在内部使用unique_ptrs的事实。

Using unique_ptr for list nodes is very much an implementation detail. Actually, even the fact that you're letting users of your list-like mechanism just use the bare list node directly - constructing it themselves and giving it to you - is not a good idea IMHO. I should not need to form a new list-node-which-is-also-a-list to add something to your list - I should just pass the payload - by value, by const lvalue ref and/or by rvalue ref. Then you deal with it. And for splicing lists - again, value, const lvalue and/or rvalue.