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.
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&) {}
}