81 Advanced The Hundred
The Exception That Lost Its Message
the same disaster, reported twice
#include <iostream>
#include <stdexcept>
void save_backup() { throw std::runtime_error("disk on fire"); }
int main() {
try {
save_backup();
} catch (std::exception e) {
std::cout << "handler A: " << e.what() << "\n";
}
try {
save_backup();
} catch (const std::exception& e) {
std::cout << "handler B: " << e.what() << "\n";
}
}
Run it. What does each handler print?
Answer
Handler A caught the fire — and lost the message.
Why¶
Handler A catches by value, so the thrown std::runtime_error is copy-constructed
into a parameter of type plain std::exception — the derived part, message included, is
sliced off (the same slicing as entry 50, relocated to a catch clause). What remains
is a genuine base-class object, so the virtual what() resolves to
std::exception::what(), which in libstdc++ returns just "std::exception". This is not
undefined behavior; it is well-defined behavior doing exactly the wrong thing, which is
why it survives code review. The slice has a rethrow twin: inside the handler, throw e;
propagates the mutilated copy, while a bare throw; rethrows the original, still-intact
exception. Handler B binds a reference to the original object, so nothing is copied and
nothing is lost.
GCC catches this: -Wall enables -Wcatch-value=, which warns catching polymorphic
type 'class std::exception' by value.
The fix¶
Takeaway: throw by value, catch by (const) reference — slicing doesn't stop at catch.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo