-1

C++11.

If I try to pop off of an empty priority_queue, then I get a compile time error. Specifically,

#include <iostream>
#include <queue>

int main()
{
    std::priority_queue<int> pq;
    int x = pq.pop();
    std::cout << x << std::endl;
}

yields the compile-time error:

In function 'int main()': 7:20: error: void value not ignored as it ought to be

I am surprised that this is a compile-time error and not a runtime error, but that is another discussion.

What I don't understand is why the top method wouldn't do the same thing. In fact, if we change pop to top the code actually compiles and runs (albeit with a bizzare output). Specifically,

#include <iostream>
#include <queue>

int main()
{
    std::priority_queue<int> pq;
    int x = pq.top();
    std::cout << x << std::endl;
}

This code compiles, runs, and exits normally, but produces no output! It literally prints nothing. I do not understand how that can be.

Here are my questions:

  1. Is the behavior for pop and top defined if the priority_queue is empty? I don't see any mention of this condition in the docs.

  2. How is there no output in the second case? top returns a constant reference. So it must be pointing at something. I would imagine that it simply interprets whatever piece of memory it is pointing to as an integer. But that doesn't seem to be happening. It seems like the integer is behaving something like a nullptr.

bremen_matt
  • 6,902
  • 7
  • 42
  • 90
  • 5
    The [`pop`](https://en.cppreference.com/w/cpp/container/priority_queue/pop) function doesn't work as you expect that is should. The error message is spot-on. And is has nothing to do with the queue being empty or not (which can't be detected by the compiler!). – Some programmer dude Jan 25 '19 at 15:31
  • 1
    And attempting to get a reference to an element that doesn't exist (with e.g. the [`top`](https://en.cppreference.com/w/cpp/container/priority_queue/top) function) leads to [*undefined behavior*](https://en.wikipedia.org/wiki/Undefined_behavior). You must always make sure that the queue is not empty before calling `top` (or `pop`). – Some programmer dude Jan 25 '19 at 15:34
  • 6
    I'm sorry but a RTFM is required here https://en.cppreference.com/w/cpp/container/priority_queue/pop – YSC Jan 25 '19 at 15:34
  • 1
    Docs say the behaviour of `top` is equal to calling `front` method on underlying container. By default the underlying container is `std::vector`, and calling `front` on empty `std::vector` is UB. – Yksisarvinen Jan 25 '19 at 15:35
  • 1
    The `pop` understanding was a dumb error. But I am confused by the UB comments – bremen_matt Jan 25 '19 at 15:38
  • @YSC Your turn to translate RTFM into some friendly like Read the fine manual, or such now ;-) – πάντα ῥεῖ Jan 25 '19 at 15:38
  • 1
    Specifically, I thought that UB means that you don't know what you get in the variable `x`. But since the code runs and terminates normally, I assumed that x must have some value. What this value is should be undefined, but my understanding is that it must be pointing at SOME piece of memory. – bremen_matt Jan 25 '19 at 15:39
  • 1
    It was not clear to me that UB behavior meant that, for instance, an integer might not even be interprettable as an integer after that point in the code. – bremen_matt Jan 25 '19 at 15:41

3 Answers3

5
int x = pq.pop();

pop returns void. It is ill-formed to assign void to a variable. As the error says:

 void value not ignored as it ought to be

On the other hand:

int x = pq.top();

It is well-formed to assign an integer to a variable.

  1. Is the behavior for pop and top defined if the priority_queue is empty?

No. The behaviour is undefined (for std::priority_queue<int>. The behaviour may be defined for other underlying containers).

  1. How is there no output in the second case?

The behaviour is undefined.

Specifically, I thought that UB means that you don't know what you get in the variable x.

That's correct, but an incomplete description of UB. UB means that you cannot predict anything about the behaviour of the program.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    I have seen cases, typically involving pointers, where clearly you can get really unexpected behavior. But that it happens in such a simple case really surprised me. – bremen_matt Jan 25 '19 at 15:45
  • 3
    @bremen_matt [Happy reading](https://stackoverflow.com/q/54120862/560648)! (Moral of the story: don't underestimate how _insanely, ridiculously, hugely complex_ is the mechanism that translates your source code to something that a computer can understand.) – Lightness Races in Orbit Jan 25 '19 at 15:51
3

The behavior of calling std::priority_queue::top on an empty priority_queue depends on the underlying container.

Reference to the top element as if obtained by a call to c.front()

As your code showed, the default argument std::vector is used as the underlying container, and the behavior of calling std::vector::front on empty container is undefined; which means anything is possible.

Calling front on an empty container is undefined.

It's similar for std::priority_queue::pop, which will call pop_back on the underlying container; for std::vector::pop_back calling it on an empty container is UB too.

Calling pop_back on an empty container is undefined.

At last, std::priority_queue::pop returns nothing; that's why int x = pq.pop(); won't work (as the error message tried to tell you).

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
3

You are using the priority_queue incorrectly. From the docs on pop():

Return value
(none)

Yet you try to bind that to an integral variable. This is the same as

void f() {};

int n = f();

and doesn't make any sense. Furthermore, for

int x = pq.top();

we again conclude from the docs that

Reference to the top element as if obtained by a call to c.front()

So the behavior depends on the front() member function of the underlying container. In your example, that's the default one, std::vector, hence from the docs:

Returns a reference to the first element in the container.
Calling front on an empty container is undefined.

There you are in UB land, hence don't try to conclude anything from the output of your program.

lubgr
  • 37,368
  • 3
  • 66
  • 117