C++ STL stack question: Why does pop() not throw an exception if the stack is empty?
Solution 1
You are correct. The C++ standard always prefers performance to safety. But there may be STL implementations that include debug range checks.
Solution 2
I would argue that the reason pop() doesn't have to throw an exception has nothing to do with efficiency or performance, but with - exceptions.
As is argued elsewhere:
SGI explanation: http://www.sgi.com/tech/stl/stack.html One might wonder why pop() returns void, instead of value_type. That is, why must one use top() and pop() to examine and remove the top element, instead of combining the two in a single member function? In fact, there is a good reason for this design. If pop() returned the top 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 top() to inspect the value at the top of the stack.
std::stack < T > is a template. If pop() returned the top element, it would have to return by value rather than by reference as per the of above explanation. That means, at the caller side it must be copied in an another T type of object. That involves a copy constructor or copy assignment operator call. What if this type T is sophisticated enough and it throws an exception during copy construction or copy assignment? In that case, the rvalue, i.e. the stack top (returned by value) is simply lost and there is no other way to retrieve it from the stack as the stack's pop operation is successfully completed!
Once we conclude that pop should not return the element it pops and thus its interface is fixed as void pop()
, it - this being my opinion - doesn't make any sense anymore to prescribe what happens when pop() is called on an empty stack.
Note that the standard requires !empty()
as precondition for calling pop().
UncleBens (in the comments) certainly has a point that not checking preconditions at runtime (which is never prescribed by the C++ std AFAIK) has a certain performance smell to it. However, to quote part of the original question: (emphasis mine)
(I'm designing a specialized Stack for my own code and would like to know the tradeoffs with this approach (which requires one to manually check if the stack is empty) vs. throwing an exception.
I will argue, that the fact that pop()
doesn't return anything renders the question moot. It (IMHO) simply doesn't make sense to force pop() to validate if the stack is empty, when we really don't get anything back from it (i.e. if the stack would be empty pop() can simply be a noop, which is (admittedly) not prescribed by the Std either).
I think one can either ask why top()
does not throw an exception or one can ask why pop()
doesn't return the top element. If pop
doesn't return anything, throwing an exception doesn't make sense (in the C++ world) -- Claiming that it doesn't throw an exception "because of the runtime costs of exceptions" like other answers seem to imply is - IMHO - missing the point.
Solution 3
As almost all features in C++, they are designed around the concept that you don't pay for what you don't use. Not all environments support exceptions, traditionally, and most notably game development.
So forcing the use of exceptions if you use std::stack would be failing one of the design guidelines. You want to be able to use stack even if exceptions are disabled.
Solution 4
I think it's worth mentioning that pop()
is allowed to throw if the stack is empty -- it's just not required to. In your stack, you could assert
that the stack wasn't empty, and that would be perfectly fine (pop
on an empty stack seems to give UB, so you can do pretty much whatever you want, really).
Solution 5
Performance aside, I don't think popping from an empty stack is an exceptional scenario - therefore I would't throw there either.
Debajit
Updated on July 17, 2022Comments
-
Debajit almost 2 years
Why does std::stack::pop() not throw an exception if the stack is empty and there is nothing to pop?
(I'm designing a specialized Stack for my own code and would like to know the tradeoffs with this approach (which requires one to manually check if the stack is empty) vs. throwing an exception.
My guess here would be that although C++ supports exception-handling, it comes with a small runtime overhead, and therefore, for maximum performance, the decision was made not to throw an exception in std::stack::pop).
-
asdfjklqwer about 13 yearsAnd it's for this reason (efficiency) that STL requires you to call value_type& top() followed by void pop(), rather than having a pop() which returns by value.
-
Debajit about 13 yearsSo in an embedded system, vector::at() and new do not throw exceptions at all?
-
Jerry Coffin about 13 years@Nathan: Those are separated for exception safety. If the copy ctor throws while returning from a pop that returns by value, you've just leaked the item that was on top of the stack (i.e., the item is lost, and you can't restore it to the stack either). See: gotw.ca/gotw/008.htm
-
Debajit about 13 yearsWhere is it documented that pop() is allowed to throw? Does the spec specify this?
-
Admin about 13 years@Jerry has it spot on! It has nothing to do with perf. In fact, forcing developers to call top before pop is worse in terms of performance...
-
asdfjklqwer about 13 years@Jerry Ah, I didn't consider that - makes perfect sense! @Moron Is top/pop not more efficient if the elements in question are expensive to copy?
-
GManNickG about 13 years@Moron: Do you have evidence for that?
-
Admin about 13 years@GMan: Of course not :-) One of the cases we talk of does not even exist! Perhaps I should have included the word "probably". And I meant to include the check for empty stack in the previous comment too.
-
Jerry Coffin about 13 years@Nocturne: §17.4.4.8/3: "Any other functions defined in the C++ Standard Library that do not have an exception-specification may throw implementation-defined exceptions unless otherwise specified." No exception specification is given for
std::stack::pop()
. It just invokespop_back
, which doesn't seem to have an exception specification either. -
Admin about 13 years@Nathan: I don't see why pop has to return by value. I meant to also include the check for empty stack in the previous comment (ok, repeated that).
-
asdfjklqwer about 13 yearsBecause it doesn't make sense to return a pointer/ref to a deleted element. But I'm guessing you know that and you're questioning whether std::stack must strictly own its contained elements, and therefore whether pop() should be responsible for deallocating an element or simply updating a stack pointer...
-
GManNickG about 13 years"lots of C++ code, no exception support from runtimes" Seems contradictory to me. C++ very clearly has exceptions, if your platform doesn't support it, you're not using C++, you're using an implementation-specific derivative of C++.
-
Admin about 13 years@Nathan: Yes, that is it. For me, the reason to pop an element off the stack would be to be able to use it! It just does not feel like the stack one learns about. Of course, having the container own the objects has its advantages.
-
Debajit about 13 yearsThanks, that was informative. Could you provide a link where I can access the C++ standard?
-
Admin about 13 yearsI would argue that it is an exceptional scenario. Given that you would be checking for the empty stack anyway (most algorithms have this check built in), trying to pop an empty stack would be an exception.
-
UncleBens about 13 yearsI fail to see, though, how choosing not to enforce preconditions at runtime has something to do with anything other than performance...
-
Omnifarious about 13 years@UncleBens - I think the exception argument is the most compelling. It's impossible for
::std::stack::pop
to satisfy the strong exception guarantee if it returns the top element. -
Martin Ba about 13 years
-
CB Bailey about 13 yearsTo me, I think the more important point is that
push_back()
is defined to performa.erase.(--a.end())
which is UB ifa.begin() == a.end()
and this is what allows "anything to happen".erase
is normally guaranteed to throw nothing forlist
and nothing if the copy constructor or assignment operator of the contained type does not throw forvector
anddeque
; these being the containers that you usually build astack
with. -
CB Bailey about 13 yearsI don't understand this argument. Whether or not a stack implementation checks whether the stack is empty before popping or not gives the difference between the program being in a valid state after the pop or not. Whether an exception is thrown and how expensive this is, isn't the main issue. The trade off is: is it worth the implementation checking whether it is valid to perform the pop or not. If the program is designed such that it is guaranteed to never pop from an empty stack the check is redundant and pure overhead. Other clients may never be sure and would have to make the check anyway.
-
Jerry Coffin about 13 years@Charles Bailey: Presumably you meant
pop
rather thanpush_back
? -
CB Bailey about 13 yearsOops, I meant
pop_back
being whatpop
calls. -
Martin Ba about 13 years@Charles : Note that the question asked "why does pop() not throw an exception" and not why does pop not check its preconditions. With the way std::stack is designed, one has to view
pop
andtop
together. And given a C++ mindset, it kind of becomes obvious that neither of them will check their preconditions. (Though in the case ofpop
it would be trivial for an implementation to make it a no-op in case of an empty stack.) Callingpop
ortop
on an empty stack is simply UB, just as accessing an out-of-bounds index of a std::vector. -
CB Bailey about 13 years@Martin: I still don't understand how your orignal argument applies. The exception safety argument is an argument for
top()
and voidpop()
overpop()
returning a value but for either interface you can choose to check pre-conditions and throw an exception or let undefined behavior happen when you pop from an empty stack. Checking pre-conditions is a necessary precursor for throwing a meaningful exception so it is valid to consider the fact that pre-conditions don't have to be checked if the interface isn't going to guarantee to throw an exception when popping from an empty stack. -
Martin Ba about 13 years@Charles: pop() is allowed to check its preconditions and it is allowed to throw. Given its interface (returning void) I just think that it doesn't make sense to specify that it should throw one. If pop() were implemented to check whether the stack be empty, it should - IMHO - just ignore the call. (And we could get into a discussion if a failed precondition check should throw an exception or should abort() or assert() ...)
-
Marc Mutz - mmutz almost 13 years+1 Thanks for being the voice of reason here. Exceptions just shouldn't be thrown in response to failed preconditions that the caller can (easily) check.
-
Raedwald over 12 years+1: it is about programming-by-contract, API design and undefined behaviour.
pop()
has undefined behviour if the stack is empty. A related answer: stackoverflow.com/questions/7390126/…