2

The documentation says that Deleter should be:

  • nothrow constructible
  • nothrow callable (because it's called from ~unique_ptr() noexcept
  • nothrow destructible (for the reason above)

My question is why uniqut_ptr is defined to allow a Deleter that may throw. E.g. the following Deleter is allowed by all unique_ptr constructors:

struct BadDeleter
{
    BadDeleter() noexcept(false) {}
    ~BadDeleter() noexcept(false) {}
    BadDeleter(const BadDeleter&) noexcept(false) {}
    BadDeleter(BadDeleter&) noexcept(false) {}
    BadDeleter(BadDeleter&&) noexcept(false) {}
    void operator()(char* p) const noexcept(false) {
        delete p;
    };
};

Live Demo

Kentzo
  • 3,881
  • 29
  • 54
  • 1
    Where exactly does it say what you claim in the first sentence? – Kerrek SB Mar 06 '18 at 21:47
  • What documentation states this? – NathanOliver Mar 06 '18 at 21:53
  • Constructors are [noexcept](http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr), reset is [noexcept](http://en.cppreference.com/w/cpp/memory/unique_ptr/reset) and destructor is [defined](http://en.cppreference.com/w/cpp/memory/unique_ptr/%7Eunique_ptr) without `noexcept(false)`. I don't see how under these definitions Deleter can throw. – Kentzo Mar 06 '18 at 21:53
  • If it throws then it's UB. `noexcept` isn't part of the function signature so you can't check if the deleter is marked that or not. – NathanOliver Mar 06 '18 at 21:56
  • noexcept is a part of the function signature, at least in C++14. – Kentzo Mar 06 '18 at 21:57
  • 1
    Ah, C++17. I forgot they added it. They might change `unique_ptr` then in a future standard. When it was first made though you couldn't tell. – NathanOliver Mar 06 '18 at 22:02
  • I'm not actually sure about my previous statement anymore: I tried the same [piece of code](https://ideone.com/EgkbQ6) with GCC and it compiles. Only fails with MSVS 2015 Update 3. Nevertheless, there are utility functions since C++11 to check whether noexcept is present. I would expect them to be used. – Kentzo Mar 06 '18 at 22:11

1 Answers1

2

The standard only defines the requirements on a uniqe_ptr's deleter based on the unique_ptr's operations. While those requirements always say things like

~unique_ptr();
Requires: The expression get_deleter()(get()) shall be well formed, shall have well-defined behavior, and shall not throw exceptions.

the standard never explicitly specifies that the deleter must have a noexcept operator().

I presume this wording was chosen to remain backwards-compatible with C++14. In that standard, noexcept was not part of a function's signature, and adding that requirement in C++17 likely would have broken a lot of code where custom deleters were used without explicitly marking their operations as noexcept.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52