Propagating exceptions from destructors with std::exception_ptr | Sandor Dargo's Blog<br>Sandor Dargo's Blog<br>On C++, software development and books
HOME TAGS ARCHIVES BOOKS SPEAKING DAILY C++ WORKSHOPS HI... SUBSCRIBE
Blog 2026 07 08 Propagating exceptions from destructors with std::exception_ptr Post<br>Cancel
Propagating exceptions from destructors with std::exception_ptr<br>Sandor Dargo Jul 8 2026-07-08T00:00:00+02:00<br>5 min
A few weeks ago, I wrote about what happens when a destructor actually throws and why it is a dangerous idea. One of the readers commented that he was once in a situation where he had to propagate an exception from a destructor. But as a destructor cannot safely throw and it also cannot return any value, he needed a better solution.<br>And that solution was std::exception_ptr.<br>Let’s look into what this type is and how it can be used.<br>What is std::exception_ptr?<br>std::exception_ptr is a nullable pointer-like type that was added in C++11. We can assign any captured exception to it with the help of std::current_exception.<br>To be precise, we can also create an instance of an exception pointer with std::make_exception_ptr. But it’s really just shorthand for throwing an exception and capturing it in a catch block before returning it.
If you wonder what std::current_exception is — it’s a utility also introduced in C++11 that returns an instance of std::exception_ptr. When called during exception handling, it captures the current exception object and wraps it in an exception_ptr.<br>If for any reason - such as low memory - it fails and throws, current_exception would return that new exception instead. But don’t worry, you cannot get into an endless loop — it’s cut off with the help of std::bad_exception.<br>Let’s look at a small example:<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>// https://godbolt.org/z/5q1rMKTee
#include<br>#include<br>#include
int main() {<br>std::vectorint> numbers{1, 2, 3};<br>std::exception_ptr eptr;
try {<br>std::cout numbers.at(66) '\n'; // Throws std::out_of_range!<br>} catch (...) {<br>eptr = std::current_exception(); // capture<br>return 0;
We can see how an exception_ptr can survive a catch block without the exception actually being handled. The exception is stored — not swallowed — so it can be dealt with later.<br>Now we can pass that pointer to a function dedicated to handling it.<br>Let’s update the previous example by adding and calling handle_exception_pointer.<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>// https://godbolt.org/z/K4hM5b1fP
#include<br>#include<br>#include
void handle_exception_pointer(std::exception_ptr eptr) {<br>try {<br>if (eptr) {<br>std::rethrow_exception(eptr);<br>} catch (const std::exception& e) {<br>std::cout "Caught exception: '" e.what() "'\n";
int main() {<br>std::vectorint> numbers{1, 2, 3};<br>std::exception_ptr eptr;
try {<br>std::cout numbers.at(66) '\n'; // Throws std::out_of_range!<br>} catch (...) {<br>eptr = std::current_exception(); // capture
handle_exception_pointer(eptr);
return 0;
A couple of remarks.<br>We take std::exception_ptr by value — after all, it’s a pointer-like type and cheap to copy.<br>At the same time, we cannot dereference it or call what() on it directly — neither with pointer nor value semantics. So if we actually want to extract the information from it, we have to use std::rethrow_exception, which rethrows the exception previously captured into the pointer. Once it’s rethrown inside the new try block, we can catch and handle it normally.<br>This separation of capture and handling is precisely what makes std::exception_ptr so useful.<br>How this can help with destructors<br>Let’s see now an example of how it can help propagate exceptions that originate in a destructor.<br>As we established, a destructor cannot safely throw an exception, and it can never have a return value. But sometimes a destructor performs cleanup operations that might fail, and silently ignoring those failures is not acceptable either.<br>The trick is to pass an empty exception_ptr — by reference — to the type whose destructor might encounter an error. The destructor then catches any exception internally (keeping it noexcept), and stores it in the exception_ptr for the caller to inspect after the object is destroyed.<br>Here is an example:<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>// https://godbolt.org/z/r17xfGW9q
#include<br>#include<br>#include
struct Raii {<br>std::exception_ptr& err;
Raii(std::exception_ptr& e) : err(e) {}
~Raii() noexcept {<br>try {<br>// let's simulate a failure<br>throw std::runtime_error("destruction failed");<br>} catch (...) {<br>if (!err) {<br>err = std::current_exception();<br>};
void handle_exception_pointer(std::exception_ptr eptr) {<br>try {<br>if (eptr) {<br>std::rethrow_exception(eptr);<br>} catch (const std::exception& e) {<br>std::cout "Caught exception: '" e.what() "'\n";
int main() {<br>std::exception_ptr err;
Raii r{err};<br>} // destructor stores exception into err
handle_exception_pointer(err);
The destructor stays noexcept — it never lets an exception escape....