Home » C++ » Why doesn't std::queue::pop return value.?

Why doesn't std::queue::pop return value.?

Posted by: admin November 29, 2017 Leave a comment

Questions:

I went through this page but I am not able to get the reason for the same . There it is mentioned that

“it is more sensible for it to return no value at all and to require
clients to use front() to inspect the value at the front of the queue”

But inspecting an element from front() also required that element to be copied in lvalue. For example in this code segment

std::queue<int> myqueue;
int myint;
int result;
std::cin >> myint;
myqueue.push (myint);

/* here temporary will be created on RHS which will be assigned to result, and in case
if returns by reference then result will be rendered invalid after pop operation */

result = myqueue.front();  //result.
std::cout << ' ' << result;
myqueue.pop();

on fifth line cout object first creates a copy of myqueue.front() then assigns that to result. So, whats the difference, pop function could have done the same thing.

Answers:

So, whats the difference, pop function could have done the same thing.

It could indeed have done the same thing. The reason it didn’t, is because a pop that returned the popped element is unsafe in the presence of exceptions (having to return by value and thus creating a copy).

Consider this scenario (with a naive/made up pop implementation, to ilustrate my point):

template<class T>
class queue {
    T* elements;
    std::size_t top_position;
    // stuff here
    T pop()
    {
        auto x = elements[top_position];
        // TODO: call destructor for elements[top_position] here
        --top_position;  // alter queue state here
        return x;        // calls T(const T&) which may throw
    }

If the copy constructor of T throws on return, you have already altered the state of the queue (top_position in my naive implementation) and the element is removed from the queue (and not returned). For all intents and purposes (no matter how you catch the exception in client code) the element at the top of the queue is lost.

This implementation is also inefficient in the case when you do not need the popped value (i.e. it creates a copy of the element that nobody will use).

This can be implemented safely and efficiently, with two separate operations (void pop and const T& front()).

Questions:
Answers:

The page you have linked to answers your question.

To quote the whole section relevant:

One might wonder why pop() returns void, instead of value_type. That is, why must one use front() and pop() to examine and remove the element at the front of the queue, instead of combining the two in a single member function? In fact, there is a good reason for this design. If pop() returned the front element, it would have to return by value rather than by reference: return by reference would create a dangling pointer. Return by value, however, is inefficient: it involves at least one redundant copy constructor call. Since it is impossible for pop() to return a value in such a way as to be both efficient and correct, it is more sensible for it to return no value at all and to require clients to use front() to inspect the value at the front of the queue.

C++ is designed with efficiency in mind, over the number of lines of code the programmer has to write.

Questions:
Answers:

pop cannot return a reference to the value that is removed, as it is being removed from the data structure, so what should the reference refer to? It could return by value, but what if the result of pop is not stored anywhere? Then time is wasted copying the value unnecessarily.

Questions:
Answers:

With the current implementation, this is valid:

int &result = myqueue.front();
std::cout << result;
myqueue.pop();

If pop would return a reference, like this:

value_type& pop();

Then the following code could crash, since the reference is not valid anymore:

int &result = myqueue.pop();
std::cout << result;

On the other hand, if it would return a value directly:

value_type pop();

Then you would need to do a copy for this code to work, which is less efficient:

int result = myqueue.pop();
std::cout << result;

Questions:
Answers:

You can totally do this:

std::cout << ' ' << myqueue.front();

Or, if you want the value in a variable, use a reference:

const auto &result = myqueue.front();
if (result > whatever) do_whatever();
std::cout << ' ' << result;

Next to that: the wording ‘more sensible’ is a subjective form of ‘we looked into usage patterns and found more need for a split’. (Rest assured: the C++ language is not evolving lightly…)

Questions:
Answers:

I think the best solution would be to add something like

std::queue::pop_and_store(value_type& value);

where value will receive the popped value.

The advantage is that it could be implemented using a move assignment operator, while using front + pop will make a copy.

Leave a Reply

Your email address will not be published. Required fields are marked *