Я поддерживаю набор 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;
^
Изменение кода для использования queue
как в этот вопрос решает проблему, и код компилируется просто отлично.
Нет ли способа сохранить unique_ptr
в priority_queue
или я что-то упустил?
std::priority_queue::top()
возвращает константную ссылку, поэтому вы не можете ее переместить. Глядя на открытый интерфейс priority_queue
нет способа получить неконстантную ссылку, которую вы можете переместить (что является обязательным для unique_ptr
, он не имеет конструктора копирования).
Решение: замещать unique_ptr
с shared_ptr
чтобы иметь возможность скопировать их (а не просто переместить их).
Или, конечно, использовать другой вид контейнера вообще (но если вы выбрали priority_queue
во-первых, это, вероятно, не приемлемо для вас).
Вы также можете использовать «взлом защищенного члена» для доступа к защищенному члену. c
(лежащий в основе контейнер), но я бы не рекомендовал его, это довольно грязно и вполне вероятно UB.
Я согласен, это невероятно раздражает. Почему это позволяет мне std::move
элементы в очередь, тогда не дайте мне способа их переместить? У нас больше нет копии оригинала, поэтому мне нужен неконстантный объект, когда я делаю top()
а также pop()
,
Решение: простираться 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
};