我在unique_ptr
中维护了一组priority_queue
个实例。在某些时候,我想获得第一个元素并将其从队列中删除。但是,这总是会产生编译器错误。请参阅下面的示例代码。
int main ()
{
std::priority_queue<std::unique_ptr<int>> queue;
queue.push(std::unique_ptr<int>(new int(42)));
std::unique_ptr<int> myInt = std::move(queue.top());
return 1;
}
这会产生以下编译器错误(gcc 4.8.0):
uptrtest.cpp: In function ‘int main()’: uptrtest.cpp:6:53: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’ std::unique_ptr<int> myInt = std::move(queue.top());
^ In file included from /usr/include/c++/4.8/memory:81:0,
from uptrtest.cpp:1: /usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
unique_ptr(const unique_ptr&) = delete;
^
在this question中更改代码以使用queue
可以解决问题并且代码编译得很好。
是否无法将unique_ptr
保留在priority_queue
中,或者我遗失了什么内容?
答案 0 :(得分:8)
std::priority_queue::top()
返回一个const引用,因此您无法移动它。查看public interface of priority_queue
,没有方法可以获取可以移动的非const引用(这对于unique_ptr
是必需的,它没有复制构造函数。)
解决方案:将unique_ptr
替换为shared_ptr
,以便能够复制它们(而不仅仅是移动它们)。
或者,当然,完全使用另一种容器(但如果你首先选择priority_queue
,这对你来说可能是不可接受的。)
您也可以使用“受保护的成员黑客”访问受保护的成员c
(底层容器),但我不推荐它,这很脏,很可能是UB。
答案 1 :(得分:6)
我同意,这非常令人讨厌。为什么它让我std::move
元素进入队列,然后让我无法将它们移出?我们不再拥有原始版本的副本,因此在执行top()
和pop()
时我需要一个非const对象。
解决方案:扩展std::priority_queue
,添加一次同时执行这两项操作的方法pop_top()
。这应该保留队列的任何顺序。它确实依赖于c ++ 11。以下实现仅适用于gcc编译器。
template<typename _Tp, typename _Sequence = std::vector<_Tp>,
typename _Compare = std::less<typename _Sequence::value_type> >
class priority_queue: std::priority_queue<_Tp, _Sequence, _Compare> {
public:
typedef typename _Sequence::value_type value_type;
public:
#if __cplusplus < 201103L
explicit
priority_queue(const _Compare& __x = _Compare(),
const _Sequence& __s = _Sequence()) :
std::priority_queue(__x, __s) {}
#else
explicit
priority_queue(const _Compare& __x, const _Sequence& __s) :
std::priority_queue<_Tp, _Sequence, _Compare>(__x, __s) {}
explicit
priority_queue(const _Compare& __x = _Compare(), _Sequence&& __s =
_Sequence()) :
std::priority_queue<_Tp, _Sequence, _Compare>(__x, std::move(__s)) {}
#endif
using std::priority_queue<_Tp, _Sequence, _Compare>::empty;
using std::priority_queue<_Tp, _Sequence, _Compare>::size;
using std::priority_queue<_Tp, _Sequence, _Compare>::top;
using std::priority_queue<_Tp, _Sequence, _Compare>::push;
using std::priority_queue<_Tp, _Sequence, _Compare>::pop;
#if __cplusplus >= 201103L
using std::priority_queue<_Tp, _Sequence, _Compare>::emplace;
using std::priority_queue<_Tp, _Sequence, _Compare>::swap;
/**
* @brief Removes and returns the first element.
*/
value_type pop_top() {
__glibcxx_requires_nonempty();
// arrange so that back contains desired
std::pop_heap(this->c.begin(), this->c.end(), this->comp);
value_type top = std::move(this->c.back());
this->c.pop_back();
return top;
}
#endif
};
答案 2 :(得分:-1)
这是另一个丑陋的解决方法。就个人而言,我比其他建议的丑陋解决方法更喜欢它。根据您的情况,您可能需要使用这一位置。将原始指针存储在优先级队列中。每次弹出时,请确保将其放在唯一的指针中。还有一个析构函数来清理剩余的东西。
这是未经测试的代码。
class Queue_with_extra_steps {
public:
void push( std::unique_ptr<int>&& value ) {
queue.push( value.release() );
}
std::unique_ptr<int> pop() {
if( queue.empty() ) {
return nullptr;
}
std::unique_ptr<int> ans( queue.top() );
queue.pop();
return ans;
}
~Queue_with_extra_steps() {
while( !queue.empty() ) {
this->pop();
}
}
// Add other functions if need be.
private:
std::priority_queue<int*> queue;
};