Long title: Why std:variant
's operator=(T&& t)
's noexcept specification doesn't depend on inner types's destructor's noexcept specification?
I can see on cppreference that
template <class T> variant& operator=(T&& t) noexcept(/* see below */);
is
noexcept(std::is_nothrow_assignable_v<T_j&, T> &&
std::is_nothrow_constructible_v<T_j, T>)
So this compiles:
struct FooThrow {
~FooThrow() noexcept(false) {throw;}
};
static_assert(std::is_nothrow_assignable_v<std::variant<FooThrow, int>, int>);
But it calls FooThrow
's destructor which is noexcept(false)
:
std::variant<FooThrow, int> x;
x = 3; // throws
It doesn't seem right. Am I missing something?
In general, standard library types do not take kindly to types that have throwing destructors. Or specifically, when a destructor actually emits an exception. There's a general rule about it ( [res.on.functions] )
Since
variant::operator=
has no special statement about throwing destructors, having those destructors actually throw is UB.