【问题标题】:Why can't `std::priority_queue::top()` return a non-const reference?为什么 `std::priority_queue::top()` 不能返回非常量引用?
【发布时间】:2020-07-31 00:31:39
【问题描述】:

我需要维护一个大对象(T 类型)的优先级队列 @9​​87654321@。由于这些对象的复制成本很高,我希望能够使用auto h = std::move(Q.top()) 检索可写对象。但我不能这样做,因为std::priority_queue<std::unique_ptr<T>>::top() 只返回一个常量引用。为什么? (有没有简单的解决方法?)

【问题讨论】:

  • 修改优先队列中的元素会破坏其作为优先队列的属性。
  • @molbdnilo 也许有一个类似pop() 的方法可以将我的对象还给我?
  • @molbdnilo 是的,我有。只是确保我有理由抛弃 const 或者我是否遗漏了什么。
  • 您可以从priority_queue派生,然后您将获得底层容器c成员(受保护)的访问权限。然后你可以随心所欲地使用c。但我不建议这样做。
  • 写入这个对象会修改它在队列中的位置吗?

标签: c++ stl c++14 priority-queue


【解决方案1】:

您可以将大对象作为unique_ptr<T> 存储在优先级队列中。需要注意的是queue.top() 返回一个const unique_ptr<T>&,这意味着T 本身不是const。所以你可以这样做:

T obj(std::move(*queue.top()));
queue.pop();

编辑:由于您的T 没有移动构造函数,我只好硬着头皮使用std::shared_ptr<T>

std::priority_queue<std::shared_ptr<T>, ...> queue;

// fill queue

// No need for anything special. 
std::shared_ptr<T> ptr = queue.top();
queue.pop();

【讨论】:

  • 好的,所以如果T 没有移动构造函数(这就是我将它放在std::unique_ptr 中的原因)我应该使用std::unique_ptr&lt;std::unique_ptr&lt;T&gt;&gt;
  • 在这种情况下我会使用std::shared_ptr&lt;T&gt;。您只需要 shared 部分将其(指针,而不是对象)复制出队列,但它会省去尝试处理指向指针的指针等的麻烦。我将编辑我的答案以包含它。
【解决方案2】:

您可以将您的大对象包装到附加结构中,该结构将有一个字段供自定义比较函数使用,该成员不应受 move 操作的影响,例如它应该受一些普通数据的影响输入int:

struct BigObject {
    std::unique_ptr<int> data;
    int forCmp;
};

struct Cmp {
    bool operator()(const BigObject& lhs, const BigObject& rhs) {
        return lhs.forCmp < rhs.forCmp;
    }
};

move(queue.top()) 之后队列的内部顺序不能被破坏。已移动的 BigObject 实例仍有一个有效值 forCmp 供比较器使用。

然后继承priority_queue,这样你就可以访问c underyling容器,并添加front方法:

template<class T, class Cmp>
struct Wrapper : std::priority_queue<T,std::vector<T>,Cmp> {
    T& front() {
        return this->c.front();
    }
};

用途:

Wrapper<BigObject,Cmp> q;
BigObject bo;
bo.forCmp = 12;
q.push(std::move(bo));

BigObject i = std::move(q.front());

Full demo

【讨论】:

    【解决方案3】:

    你可以从priority_queue继承并写T pop()来隐藏void pop()

    template <typename T>
    T fixed_priority_queue<T>::pop() {
        std::pop_heap(c.begin(), c.end(), comp); 
        T value = std::move(c.back());
        c.pop_back();
        return value;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      • 2016-03-15
      • 1970-01-01
      • 2020-07-06
      • 2018-09-18
      • 2015-10-03
      相关资源
      最近更新 更多