Re: Why does std::stack::pop() not throw an exception if the stack is empty?
On Feb 3, 1:51 pm, Debajit Adhikary <debaj...@gmail.com> wrote:
Why does std::stack::pop() not throw an exception if the stack is empty a=
nd 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).
You could be right under a very particular interpretation of your
words. Allow me to clarify, to ensure we're on the same page.
It's quite possible to implement exceptions so that the following two
programs will have basically the same runtime when passed no (command
line) arguments.
//program 1
int main(int argc, char** argv)
{ return (argc == 3) ? 1 : 0;
}
//program 2
int main(int argc, char** argv)
{ try
{ if (argc == 3)
throw 1;
}catch (int )
{ return 1;
}
return 0;
}
Let me emphasis that this is quite possible. A standard modern linux
and gcc distro on X86_64 has this behavior. Now, on the codepath where
argc == 3, then the first program is likely much faster than the
second. This is what most people say when they're talking about
"exception overhead", that is the overhead of having a possible throw,
but not actually executing it. To repeat, on a good implementation
like gcc on linux, if you don't throw, you don't pay a penalty.
On windows (32 and 64) and some other platforms, the situation is
different. Sadly, the compiler writers and ABI writers for those
platforms entirely missed the memo on how exceptions ought to be
implemented, and as such, you will see a measurable difference between
the runtime for programs like the above. Obviously not just for one
iteration, but if you have a program on windows that has possible
throw statements which are never executed, you can get measurably
faster code if you change the throws to return codes. Sad but true.
So, with that out of the way, there's one more issue to deal with. Why
does pop not throw an exception? Because even on the "ideal"
implementation of gcc on linux, there is still overhead to throw
because you must check the condition. The exception part is just a red
herring. The answer is the same even if you asked "Why doesn't pop
return an error code when the stack is empty?". Internally, it has to
have code like:
void stack::pop()
{ if (is_empty())
throw something;
do_pop();
}
The execution of "is_empty()" on every pop is likely unnecessary for a
large majority of code, but it takes cycles even if it's not
necessary. The exception vs return code overhead discussion is
entirely irrelevant. It's a discussion of whether it's worth it to
check preconditions or call it UB. The C++ standard philosophy is to
only pay for what you use, and to provide building blocks for
developers. If the developer want to use a stack which has pop throw
on an empty stack, they're welcome to wrap std::stack. However, if it
was the reverse, you couldn't wrap std::stack and get rid of the
"is_empty()" check if it was inside std::stack::pop, which is exactly
why it's not there.
PS: There is actual overhead for exceptions even on the ideal
implementation. The overhead is not execution time, but size. That
overhead is size of executable, and size of virtual, not resident,
memory. Those exception handlers have to go somewhere. If you have
virtual memory, it's almost a non-issue for a good implementation - if
you don't throw an exception, then the exception handlers never get
loaded to main memory, and it's almost as if they didn't even exist.
However, for platforms without virtual memory, such as some embedded
platforms, that memory overhead may be unacceptable.