最小的工作示例。
#include <cassert>
#include <list>
#include <queue>
//#define USE_PQ
struct MyClass
{
const char* str;
MyClass(const char* _str) : str(_str) {}
MyClass(MyClass&& src) { str = src.str; src.str = nullptr; }
MyClass(const MyClass&) = delete;
};
struct cmp_func
{
bool operator() (const MyClass&, const MyClass&) const
{
return true;
}
};
typedef std::priority_queue<MyClass, std::vector<MyClass>, cmp_func> pq_type;
#ifdef USE_PQ
MyClass remove_front(pq_type& l)
{
MyClass moved = std::move(l.top());
// error from the above line:
// use of deleted function ‘MyClass::MyClass(const MyClass&)’
l.pop();
return std::move(moved);
}
#else
MyClass remove_front(std::list<MyClass>& l)
{
MyClass moved = std::move(l.front());
l.erase(l.begin());
return std::move(moved);
}
#endif
int main()
{
const char* hello_str = "Hello World!";
MyClass first(hello_str);
#ifdef USE_PQ
pq_type l;
l.push(std::move(first));
MyClass moved = remove_front(l);
#else
std::list<MyClass> l;
l.push_back(std::move(first));
MyClass moved = remove_front(l);
#endif
assert(moved.str);
assert(!first.str);
return 0;
}
所以这可行。现在从第4行删除注释符号,它说需要复制构造函数(删除了我的)。此外,它错过了
operator=
。问题:注意:您也可以使用boost的priority_queue作为答案,但是我也遇到了同样的错误。
最佳答案
这似乎是std::priority_queue<T>
设计的疏忽。似乎没有一种方法可以将元素直接移出(而不是复制)。问题在于top()
返回const T&
,因此无法绑定(bind)到T&&
。而且pop()
返回void
,因此您也无法从中获得它。
但是,有一种解决方法。保证优先级队列中的对象实际上不是const
一样好。它们是普通的对象,队列只是不能对它们进行可变的访问。因此,这样做是完全合法的:
MyClass moved = std::move(const_cast<MyClass&>(l.top()));
l.pop();
正如@DyP在注释中指出的那样,您应该确保从对象移出仍然可以传递到队列的比较器。而且我相信,为了保留队列的先决条件,它必须与以前进行比较(这几乎是不可能实现的)。
因此,您应该将
cast & top()
和pop()
调用封装在一个函数中,并确保在此之间不对队列进行任何修改。如果这样做,则可以合理确定比较器将不会在移出的对象上被调用。当然,这种功能应该有充分的文档证明。
请注意,每当为类提供自定义复制/move 构造函数时,都应提供相应的复制/move 赋值运算符(否则,该类的行为可能会不一致)。因此,只需给您的类(class)一个已删除的拷贝分配运算符和一个适当的 move 分配运算符即可。
(注意:是的,在某些情况下,您需要一个可 move 构造的类,而不是可 move 分配的类,但这种情况极为罕见(一旦找到它们,您就会知道它们)。作为经验法则,始终同时提供ctor和工作分配op)
关于c++ - 在C++ 11中移出std priority_queue的元素,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20149471/