C++ 如何将唯一的_ptr参数传递给构造函数或函数?
我不熟悉C++11中的移动语义,我不太知道如何处理构造函数或函数中的C++ 如何将唯一的_ptr参数传递给构造函数或函数?,c++,arguments,c++11,unique-ptr,C++,Arguments,C++11,Unique Ptr,我不熟悉C++11中的移动语义,我不太知道如何处理构造函数或函数中的unique\u ptr参数。考虑这个类本身: #包括 阶级基础 { 公众: typedef unique_ptr UPtr; Base(){} Base(Base::uptrn):下一个(std::move(n)){} 虚拟~Base(){} void setNext(基::UPtr n) { next=std::move(n); } 受保护的: Base::UPtr next; }; 这就是我应该如何编写带有唯一\u pt
unique\u ptr
参数。考虑这个类本身:
#包括
阶级基础
{
公众:
typedef unique_ptr UPtr;
Base(){}
Base(Base::uptrn):下一个(std::move(n)){}
虚拟~Base(){}
void setNext(基::UPtr n)
{
next=std::move(n);
}
受保护的:
Base::UPtr next;
};
这就是我应该如何编写带有唯一\u ptr
参数的函数的方法吗
我是否需要在调用代码中使用std::move
Base::uptrb1;
Base::UPtr b2(新Base());
b1->setNext(b2)//我应该写b1->setNext(std::move(b2));相反
是的,如果您在构造函数中按值获取唯一\u ptr
,则必须这样做。明确是件好事。由于unique\u ptr
是不可复制的(私有副本),因此您编写的内容应该会导致编译器错误
Base(Base::UPtr n):next(std::move(n)) {}
应该比现在好多了
Base(Base::UPtr&& n):next(std::forward<Base::UPtr>(n)) {}
应该是
void setNext(Base::UPtr&& n)
身体一样
而且。。。什么是
handle()
?编辑:这个答案是错误的,尽管严格来说,代码是有效的。我之所以把它留在这里,是因为下面的讨论太有用了。另一个答案是我上次编辑时给出的最佳答案:
::std::move
的基本思想是,向您传递唯一\u ptr
的人应该使用它来表达他们知道他们传递的唯一\u ptr
将失去所有权的知识
这意味着您应该在方法中使用对unique\u ptr
的右值引用,而不是unique\u ptr
本身。这无论如何都不会起作用,因为传入一个普通的旧unique\u ptr
需要制作一个副本,这在unique\u ptr
的界面中是明确禁止的。有趣的是,使用命名的右值引用会将其再次转换为左值,因此您还需要在方法内部使用::std::move
这意味着您的两种方法应如下所示:
Base(Base::UPtr &&n) : next(::std::move(n)) {} // Spaces for readability
void setNext(Base::UPtr &&n) { next = ::std::move(n); }
Base newBase(std::unique_ptr<Base>(new Base)); //Illegal in this case.
然后使用这些方法的人会这样做:
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
表示指针将在最相关和最有用的位置失去所有权。如果这种情况发生在不可见的情况下,那么使用您的类的人会因为没有明显的原因而突然失去所有权,这将非常令人困惑。以下是将唯一指针作为参数的可能方法,以及它们的相关含义
(A) 按价值
必须对实际l值(命名变量)调用此函数。不能使用这样的临时名称调用它:
Base(Base::UPtr &&n) : next(::std::move(n)) {} // Spaces for readability
void setNext(Base::UPtr &&n) { next = ::std::move(n); }
Base newBase(std::unique_ptr<Base>(new Base)); //Illegal in this case.
不能保证nextBase
为空。它可能是空的;可能不会。这实际上取决于Base::Base(std::unique\u ptr&n)
想要做什么。正因为如此,仅仅从函数签名就不太清楚将要发生什么;您必须阅读实现(或相关文档)
因此,我不建议将其作为接口
(C) 按常量l值引用
这与“非常量l值参考”的情况大致相同。区别在于两件事
Base newBase(std::unique_ptr<Base>(new Base)); //legal now..
您有一个合理的期望,在此行完成后,nextBase
应该为空。应该把它从这里移走。毕竟,你有一个std::move
坐在那里,告诉你已经发生了移动
问题是它没有。不能保证它已从中移出。它可能已从中移出,但只有通过查看源代码才能知道。您不能仅从函数签名来判断
建议
- (A)按值:如果您想让函数声明对
的所有权,请按值获取唯一\u ptr
- (C)按常量l值引用:如果您的意思是某个函数在该函数执行期间仅使用
,请按唯一的\u ptr
使用它。或者,将常量&
或&
传递给指向的实际类型,而不是使用常量&
唯一\u ptr
- (D)按r值引用:如果函数可以或不可以声明所有权(取决于内部代码路径),则通过
获取它。但我强烈建议尽可能不要这样做&
唯一\u ptr
。你只能移动它。正确的方法是使用std::move
标准库函数
如果您按值获取一个唯一的\u ptr
,您可以从中自由移动。但是移动实际上并没有发生,因为std::move
。采取以下声明:
std::unique_ptr<Base> newPtr(std::move(oldPtr));
std::unique_ptr newPtr(std::move(oldPtr));
这实际上是两种说法:
std::unique_ptr<Base> &&temporary = std::move(oldPtr);
std::unique_ptr<Base> newPtr(temporary);
std::unique\u ptr&&temporary=std::move(oldPtr);
std::唯一性(临时性);
(注意:以上代码在技术上并不编译,因为非临时的r值引用实际上不是r值。这里仅用于演示目的)
temporary
只是对oldPtr
的一个r值引用。移动发生在newPtr
的构造函数中unique\u ptr
的移动构造函数(将&&
带到自身的构造函数)是实际移动的对象
如果您有一个
unique\u ptr
值,并且希望将其存储在某个位置,则必须使用std::move
来进行存储。让我尝试说明将指针传递到内存由std::unique\u ptr
类模板实例管理的对象的不同可行模式;它也适用于t
Base(std::unique_ptr<Base> const &n);
Base(std::unique_ptr<Base> &&n)
: next(std::move(n)) {}
Base newBase(std::unique_ptr<Base>(new Base)); //legal now..
Base newBase(std::move(nextBase));
std::unique_ptr<Base> newPtr(std::move(oldPtr));
std::unique_ptr<Base> &&temporary = std::move(oldPtr);
std::unique_ptr<Base> newPtr(temporary);
struct node;
typedef std::unique_ptr<node> list;
struct node { int entry; list next; }
size_t length(const node* p)
{ size_t l=0; for ( ; p!=nullptr; p=p->next.get()) ++l; return l; }
list copy(const node* p)
{ return list( p==nullptr ? nullptr : new node{p->entry,copy(p->next.get())} ); }
void f(std::shared_ptr<X> x) // call by shared cash
{ container.insert(std::move(x)); } // store shared pointer in container
void client()
{ std::shared_ptr<X> p = std::make_shared<X>(args);
f(p); // lvalue argument; store pointer in container but keep a copy
f(std::make_shared<X>(args)); // prvalue argument; fresh pointer is just stored away
f(std::move(p)); // xvalue argument; p is transferred to container and left null
}
void prepend (int x, list& l) { l = list( new node{ x, std::move(l)} ); }
void remove_first(int x, list& l)
{ list* p = &l;
while ((*p).get()!=nullptr and (*p)->entry!=x)
p = &(*p)->next;
if ((*p).get()!=nullptr)
(*p).reset((*p)->next.release()); // or equivalent: *p = std::move((*p)->next);
}
void append (list& a, list&& b)
{ list* p=&a;
while ((*p).get()!=nullptr) // find end of list a
p=&(*p)->next;
*p = std::move(b); // attach b; the variable b relinquishes ownership here
}
list reversed (list&& l) noexcept // pilfering reversal of list
{ list p(l.release()); // move list into temporary for traversal
list result(nullptr);
while (p.get()!=nullptr)
{ // permute: result --> p->next --> p --> (cycle to result)
result.swap(p->next);
result.swap(p);
}
return result;
}