Skip to content

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: std::exception
handler B: disk on fire

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

} catch (const std::exception& e) {   // reference to the real thing — no copy, no slice

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

Open in Compiler Explorer ↗ Quiz this entry