In C++11, my understanding is that by default destructors are implicitly noexcept(true)
, except that:
If I have a class C
that has a destructor explicitly marked noexcept(false)
(presumably because it throws for some odd reason, and I'm aware that you shouldn't, and why) then the destructor of any class that derives from C
or contains a member of type C
also becomes noexcept(false)
.
However, a class which contains a std::shared_ptr<C>
apparently does not automatically have its destructor toggled to noexcept(false)
, and the same is true for containing a std::weak_ptr<C>
, std::unique_ptr<C>
, etc.
Here is a complete example:
#include <type_traits>
#include <memory>
struct Normal {
~Normal() {
}
};
struct ThrowsInDtor {
~ThrowsInDtor() noexcept(false) {
throw 42;
}
};
template<typename T>
struct Wrapper {
T t;
};
template<typename T>
struct UniquePtrWrapper {
std::unique_ptr<T> t;
};
template<typename T>
struct SharedPtrWrapper {
std::shared_ptr<T> t;
};
static_assert(std::is_nothrow_destructible<Normal>::value, "A"); // OK
static_assert(!std::is_nothrow_destructible<ThrowsInDtor>::value, "B"); // OK
static_assert(std::is_nothrow_destructible<Wrapper<Normal>>::value, "C"); // OK
static_assert(!std::is_nothrow_destructible<Wrapper<ThrowsInDtor>>::value, "D"); // OK
static_assert(std::is_nothrow_destructible<UniquePtrWrapper<Normal>>::value, "E"); // OK
static_assert(!std::is_nothrow_destructible<UniquePtrWrapper<ThrowsInDtor>>::value, "F"); // FAILS
static_assert(std::is_nothrow_destructible<SharedPtrWrapper<Normal>>::value, "G"); // OK
static_assert(!std::is_nothrow_destructible<SharedPtrWrapper<ThrowsInDtor>>::value, "H"); // FAILS
It seems odd to me that F and H fail. I had expected that the noexcept status of the destructor of the owned/referenced type would propagate to the smart pointer destructor, presumably by way of a noexcept expression like noexcept(std::is_nothrow_destructible<T>::value)
on the smart pointer destructor declaration.
However the standard makes no mention of this, and the standard library code I've looked at doesn't do this.
Does anyone know why it is that the standard smart pointers don't propagate the noexcept destructor status of the instantiating type to the smart pointer destructor?
std::shared_ptr<T>
is designed to be usable with an incomplete T
, hence there is no way to get the information you are asking for when declaring its destructor. Also, you can do this:
std::shared_ptr<void> dummy = std::make_shared<T>(); // for some complete T
Now what should noexcept
say for std::shared_ptr<void>
? It's a run-time-information from std::shared_ptr
's POV.
For std::unique_ptr
, there is
20.7.1.2.2 unique_ptr destructor [unique.ptr.single.dtor]
1
~unique_ptr();
Requires: The expression
get_deleter()(get())
shall be well formed, shall have well-defined behavior, and shall not throw exceptions. [ Note: The use ofdefault_delete
requiresT
to be a complete type. —endnote]
Which means that the deleter needs to make sure that it doesn't throw - which does not necessarily depend on the destructor of T
, i.e., when you use a null-deleter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With