如何使用用户定义的对象从priority_queue中获取非const顶级元素

How to get a non-const top element from priority_queue with user-defined objects?

本文关键字:获取 const 元素 queue 定义 用户 何使用 对象 priority      更新时间:2023-10-16

std::priority_queue::top返回一个常量值。但是,我想从优先级队列中删除顶部元素,并能够在其他地方修改它。

priority_queue<SomeClass, vector<SomeClass>, SomeClassCompare > pQueue;
...
SomeClass *toBeModified = &(pQueue.top());
pQueue.pop();
toBeModified->setMember(3); // I would like to do this

是否有一种方法可以从优先队列中抓取顶部元素(并从队列中删除)并按我的意愿修改它?

标准容器和容器适配器具有值语义。当您将元素压入队列时,将创建一个副本。当您从队列中删除一个对象时,该对象将被销毁。

即使top()返回给你一个对非const的引用,一旦你从队列中删除元素,这个引用就会变成悬空的,解引用会导致未定义的行为。

这就是说,std::priority_queue返回一个对const的引用,以防止您混淆(有意或无意)其内部排序-这与关联容器(如std::mapstd::set)的键为const的原因几乎相同。

您可以做的是构造一个副本 top()返回的值,修改该副本,删除原始副本,并将副本推入队列:

SomeClass obj = pQueue.top();
pQueue.pop();
obj.setMember(42);
pQueue.push(std::move(obj)); // You can move obj into the queue if you no more need it
另一方面,如果您需要引用语义,则必须将指针推入队列(可能是智能指针,取决于您的用例),并提供适当的自定义排序标准,该标准将根据指针指向的对象的属性对这些指针进行排序。在这种情况下,请注意不要在运行时修改这些属性,以免改变它们的顺序。这将被视为"扰乱了容器的内部排序",并将导致未定义的行为。

我认为值语义在这里不起任何作用。所有其他容器都具有相同的值语义,并且几乎所有容器都为front()提供可变引用。

priority_queue不允许修改top()元素有一个确切的原因:这是因为特定元素位于顶部,因为它根据其当前值相应地被限定了。优先级队列中的元素总是根据为队列配置的比较标准(操作符<默认情况下)。通过更改元素,您可能会破坏此条件,并导致所有使用排序前提条件的操作都将导致未定义的行为。>

简而言之,priority_queue不允许修改包含元素的原因与setmap的关键字的情况完全相同。

我知道您可能有一些专用的比较方法,您使用包含要比较的值的字段,并且您将修改对象的任何内容,除了这个字段。这样就不会违反排序顺序的要求。您可以做两件事:

  1. 将类的部分修改为mutable。通过这种方式,您可以通过top()获取元素并修改可变内容,即使这是const。

  2. 通过派生std::priority_queue创建自己的优先级队列类。它有一个名为c的字段(尽管在protected section中),其中包含对底层容器的引用-底层容器总是有一个front()方法,该方法访问priority_queue中的top()相同的元素。但是,为了您自己的安全,您应该将密钥字段设置为const,以便在构建期间设置并且永远不会更改-只是为了将风险降到最低。

啊,这个问题也不能通过使用指针来解决——指向的对象将是完全相同的常量。这些"引用语义"也不会以任何方式帮助您,因为如果您希望有一个专用的比较方法,它将不得不查看对象的内容,这样您将遇到完全相同的问题。如果您仅仅依赖于比较指针值,这将对您有所帮助,但我更怀疑这可能是99%情况下的解决方案。