this is example inspired by example from cppreference
struct S {
operator int() { throw 42; }
};
int main(){
variant<float, int> v{12.f}; // OK
cout << std::boolalpha << v.valueless_by_exception() << "\n";
try{
v.emplace<1>(S()); // v may be valueless
}
catch(...){
}
cout << std::boolalpha << v.valueless_by_exception() << "\n";
}
For one compiler I tried it outputs
false, true
meaning that emplace caused the variant to become valueless
What I do not understand is how this happened.
In particular I do not understand why emplace is called at all, I would expect the program to not even call it since conversion from S to int argument throws.
Note the signature for the relevant std::variant::emplace overload:
template <size_t I, class... Args>
std::variant_alternative_t<I, variant>& emplace(Args&&... args);
It takes a pack of forwarding references. This means that the conversion operator from S to int is not called when evaluating the function arguments; it's called inside the body of emplace. Since trying to construct the int in place would then fail, the variant is made valueless by exception.
It could perhaps be possible to implement variant such that for trivially movable types, the old value is saved before in place construction is attempted, and then restored if it failed, but I'm not sure if it fits in with the various restrictions on the type's implementation given by the standard.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With