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

时间:2013-05-25 23:11:11

标签: c++ c++11 const priority-queue pop

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

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

有没有办法可以从优先级队列中获取top元素(并从队列中删除)并按照我的意愿修改它?

2 个答案:

答案 0 :(得分:14)

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

即使top()会返回对非const的引用,只要从队列中删除该元素,该引用就会变为悬空,并且取消引用它将导致未定义的行为。< / p>

这就是说,std::priority_queue会返回const的引用,以防止您(有意或无意)使用其内部排序 - 这与关联容器的键的原因大致相同例如std::mapstd::setconst

您可以执行的操作是构造top()返回的值的副本,修改该副本,删除原始文件,然后将副本推送到队列中:< / p>

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

另一方面,如果你需要引用语义,那么你必须将指针推入队列(可能是智能指针,具体取决于你的用例)和提供适当的自定义排序标准,根据它们指向的对象的属性对这些指针进行排序。

在这种情况下,请注意不要在运行时修改这些属性,以使它们的顺序不同。这将被视为“搞乱容器的内部排序”,并将导致未定义的行为。

答案 1 :(得分:1)

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

priority_queue禁止修改top()元素有一个确切原因:这是因为特定元素位于顶部,因为根据其现值,它已经相应地符合条件。根据为队列配置的比较条件(运算符&lt;默认情况下),优先级队列中的元素始终排序。通过更改元素,您可能会破坏此条件,并导致使用已排序前置条件的所有操作都会导致未定义的行为。

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

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

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

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

  3. 啊,这个问题也不能通过使用指针来解决 - 指向对象将是完全相同的常量。这些“引用语义”也不会对你有任何帮助,因为如果你想要一个专用的比较方法,就必须查看对象的内容,这样你就会遇到完全相同的问题。如果你只依靠比较指针值,这对你有所帮助,但我更怀疑这可能是99%案例的解决方案。