我是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突然失去了所有权。
投票最多的答案。我更喜欢通过右值引用传递。
我理解传递右值引用可能导致的问题。但是让我们把这个问题一分为二:
调用者:
我必须写代码基准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.