1

Apparently, throwing and catching std::optional<std::exception>::value() cannot be handled polymorphically. First if we consider

try
{
    throw std::logic_error("blah ..");
}
catch(const std::exception& e)
{
    std::cout << "E: " << e.what() << std::endl; // OK, outputs "blah .."
}

We get get the expect output, but if we try

std::optional<std::exception> error = std::logic_error("Booo ..");
try
{
    throw error.value();
}
catch(const std::exception& e)
{
    std::cout << "E: " << e.what() << std::endl; // NOPE, outputs "std::exception"
}

The exception object got sliced.

I understand that this is because I'm actually throwing optional's template type, which is std::exception and not std::logic_error. That is, we basically changed the throw expression in the first example to

std::exception err = std::logic_error("Blah ..");
throw err;

which gives the same behavior.

So how can I make my std::optional<std::exception>::value() return a derived exception? Does it have to contain a pointer?

Wololo
  • 1,249
  • 1
  • 13
  • 25
  • This cannot be done with `std::optional`, since the C++ standard requires `std::optional` to not use any dynamic allocations, i.e. the object is stored as a member of the object itself, not using a pointer to an object. You can never prevent object slicing when using `std::optional` for this reason: there just isn't enough memory for the object to store other, potentially larger subclasses of the object wrapped in `std::optional`... – fabian Jun 15 '23 at 17:52

1 Answers1

4

If you want to pass exceptions around in a value type that may or may not hold them, along with type erasure of the exception type, you turned to the wrong tool from the standard library. That's what std::exception_ptr is for: it exists to pass a thrown (or ready to to be thrown) exception handle around.

auto error = std::make_exception_ptr(std::logic_error("blah .."));
// stuff 
try {
  std::rethrow_exception(std::move(error));
} catch (std::exception const& e) {
    std::cout << "E: " << e.what() << std::endl; 
}

Here it is live on godbolt.

std::exception_ptr is how the standard library bridges the gap between library code that needs to move exceptions around points of execution, and the core exception mechanism. Due to this, it isn't limited to just exceptions derived from std::exception, as we can throw arbitrary types in C++.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458