Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do the std smart pointer type destructors not inherit the noexcept dtor status of the pointed to object

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?

like image 576
acm Avatar asked Oct 22 '13 21:10

acm


1 Answers

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 of default_delete requires T 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.

like image 126
Daniel Frey Avatar answered Oct 26 '22 03:10

Daniel Frey