我有一些管理 unique_ptrs 队列的代码。在其主循环中,它从队列中弹出一个条目,对其执行某些操作,然后允许该元素超出范围并被删除。到目前为止,没有问题。
我想添加一个功能,我们可以将其移动到其他地方,而不是必须删除该元素。问题是只有元素本身知道是否执行此操作或它需要去哪里。
主循环类似于:
std::deque< std::unique_ptr<Object> > queue;
while( !queue.empty() ) {
auto element = std::move( queue.front() );
queue.pop_front();
element->do_something();
element->recycle( std::move(element) ); // ?
}
Run Code Online (Sandbox Code Playgroud)
如果回收不是对象的方法,这将不是问题,但因为它是对象的方法,我不确定它应该是什么样子。通过上面的代码,我会得到类似的东西:
class Object {
public:
virtual void do_something();
virtual void recycle( std::unique_ptr<Object> unique_this ) {
// move unique_this somewhere, or don't and allow it to be deleted
}
};
Run Code Online (Sandbox Code Playgroud)
这安全吗?如果recycle不移动unique_this,它将在它自己的方法之一中删除它(不过在方法的末尾)。那是问题吗?
我想到避免删除这个的一种可能性是让recycle通过左值引用获取unique_ptr,我意识到这有点奇怪。这个想法是,如果它愿意,它会将其移动到某个地方,但如果不这样做,则直到回收返回后该对象才会被删除。不过,它不一定避免在其方法之一中可能删除对象的问题,因为我们必须确保无论我们将其移动到何处都不会立即删除它。
如果这样做不安全,后备计划是让 Object 返回一个回收器(即,一个不是执行回收的 Object 方法的可调用对象)。我认为这应该是可能的,但是考虑到不同的对象想要使用不同的回收器,管理回收器的类型和生命周期会有点混乱。
在我看来,第一个想法(按值传递 unqiue_ptr)是最好的,除非它违反任何规则。这样安全吗?有一个更好的方法吗?
element->recycle(std::move(element));在 C++17 之前是有问题的:
element->可能在移动构造函数之后求值,因此是nullptr。太不安全了。
那么更安全的做法是更改为:
virtual void recycle( std::unique_ptr<Object>&& unique_this )
Run Code Online (Sandbox Code Playgroud)
请记住,“移动”的不是std::move“移动”,而是移动结构。
选择
auto ptr = element.get(); // element might become nullptr just below,
// so kept "old" non-nullptr value
ptr->recycle(std::move(element));
Run Code Online (Sandbox Code Playgroud)
是可能的,但容易出错(需要注释,或者可能会在错误的代码中严重重构)
自 C++17 起,求值顺序 (13)指定element->在求值参数之前求值;所以它是安全的。