Search code examples
c++standards-compliance

May the elements in a std::vector have a throwing destructor?


When I look at the Container requirements on cppreference it lists Destructible as a requirement for value_type. This seems to imply that destructors of container elements may not throw.

I haven't been able to find a citation for this requirement in the C++14 standard (haven't looked in older versions). The only thing I can find is that value_type must be Erasable which doesn't imply any exception safety at all.

So my question is, may the elements in a std::vector have a throwing destructor? If not, what section in the standard prohibits it?


P.S.: Don't worry, I'm not planning to create types with throwing destructors. I'm just writing a standard-conforming implementation and trying to get exception safety right.


Solution

  • N4140 [res.on.functions]/2 states:

    In particular, the effects are undefined in the following cases:

    (2.1) — for replacement functions (18.6.1), if the installed replacement function does not implement the semantics of the applicable Required behavior: paragraph.

    (2.2) — for handler functions (18.6.2.3, 18.8.3.1, D.11.1), if the installed handler function does not implement the semantics of the applicable Required behavior: paragraph

    (2.3) — for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (17.6.3.5, 23.2, 24.2, 26.2). Operations on such types can report a failure by throwing an exception unless otherwise specified.

    (2.4) — if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.

    (2.5) — if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

    Which is a bit obscure, but saves a lot of space that would otherwise be wasted on "T must meet the Destructible requirements" statements throughout the library clauses.

    Notably, this does not imply that elements of a std::vector can't have a throwing destructor; it only means that said destructor must never throw when called from the standard library. So e.g. this program is conforming:

    #include <vector>
    
    struct A {
      bool throw_an_int = false;
      ~A() noexcept(false) {
        if (throw_an_int) throw 42;
      }
    };
    
    int main() {
      try {
        A a; 
        a.throw_an_int = true;
        std::vector<A> lots_of_As(42);
      } catch(int&) {}
    }