While the shortviewed solution is to return a value, it is better if a pop()
is of type void
.
template <typename T>
void Stack<T>::pop() {
...
}
The reason is that you cannot write an exception safe pop()
-function that returns something:
template <typename T>
T Stack<T>::pop() {
T foo = stor_.top(); // May throw.
stor_.pop(); // This line shall never throw.
return foo; // May throw.
}
The second line (with the pop()
) should never throw something, because destructors shall not throw. So it is sane to assume that line is nothrow and will always succeed (except when you pop an empty container).
The first and third line may throw because data is being copied.
Now imagine that
T foo = stor_.top(); // May throw.
stor_.pop(); // This line shall never throw.
run flawlessly: You have a value ready to be returned, and you have successfully changed the state of the holding object.
But then
return foo; // May throw.
bang it throws an exception. What happened:
- The function did not succeed, the caller got nothing except an exception
- Still yet, the state of the object has been modified.
- And noone has an up to date copy of the lost object anymore which typically contains critical business data that the boss phoned over seconds ago before having a fatal accident
This is contradictory to Abrahams Guarantees:
The no-throw guarantee: that the operation will not throw an exception.
The strong guarantee: that the operation has either completed successfully or thrown an exception, leaving the program state exactly as it was before the operation started.
The basic guarantee: that the invariants of the component are preserved, and no resources are leaked. Often referred to as the weak guarantee, because following an exception the system is left in a safe, but unknown, state.
And with a pop()
that returns a value, you cannot guarantee that no information is lost.
See also GotW #8 and GotW #82