Info
-
Did you know that
std::variant
become valueless by exception?-
(guaranteed) an exception is thrown during the move initialization of the contained value from the temporary in copy assignment
-
(guaranteed) an exception is thrown during the move initialization of the contained value during move assignment
-
(optionally) an exception is thrown when initializing the contained value during a type-changing assignment
-
(optionally) an exception is thrown when initializing the contained value during a type-changing emplace
-
Example
struct foo {
foo() = default;
foo(const foo&) { throw 42; }
};
int main() {
std::variant<int, foo> v{42};
assert(not v.valueless_by_exception());
try {
v = foo{}; // throws
} catch(...) { }
assert(v.valueless_by_exception());
}
Puzzle
- Can you implement
throws
class in order to satisfy guaranteed variant valueless by exception tests?
int main() {
using namespace boost::ut;
using namespace boost::ut::spec;
describe("variant - valuless by exception") = [] {
it("should valuless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws; // TODO
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
// TODO
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valuless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws; // TODO
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
// TODO
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
}
Solutions
describe("variant - valuless by exception") = [] {
it("should valueless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws {
throws() = default;
throws(const throws&) { throw 42; }
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
throws t{};
v = t;
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valueless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws {
throws() = default;
throws(throws&&) { throw 42; }
throws& operator=(throws&&) = default;
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
describe("variant - valuless by exception") = [] {
it("should valueless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws {
throws() = default;
throws(const throws&) { throw std::exception{}; }
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valueless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws {
throws() = default;
throws(throws&&) { throw std::exception{}; }
throws& operator=(throws&&) = default;
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
describe("variant - valuless by exception") = [] {
it("should valuless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws {
constexpr throws() = default;
throws(throws const&) { throw "an error"; }
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
auto const t = throws{};
v = t;
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valuless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws {
constexpr throws() = default;
throws(throws&&) { throw "an error"; }
constexpr throws& operator=(throws&&) = default;
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
describe("variant - valueless by exception") = [] {
it("should valuless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws
{
throws() = default;
throws(const throws& t) {throw 123;}
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
throws tmp{};
v = tmp;
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valueless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws
{
throws() {}
throws(throws&&) {throw 123;}
throws& operator=(throws&&) = default;
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
describe("variant - valuless by exception") = [] {
it("should valuless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws{
throws() = default;
throws( throws const & ) {throw 0;}
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
throws a;
v = a;
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valuless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws{
throws() = default;
throws( throws && ) {
throw 0;
}
throws & operator=( throws && )
{
throw 1;
return *this;
}
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
throws a;
v = std::move(a);
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};
describe("variant - valuless by exception") = [] {
it("should valuless by exception when an exception is thrown during the move initialization of the contained value from the temporary in copy assignment") = [] {
struct throws {
throws() = default;
throws(throws const&) { throw 42; }
};
std::variant<std::monostate, throws> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = throws{};
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
it("should valuless by exception when an exception is thrown during the move initialization of the contained value during move assignment") = [] {
struct throws {
throws() = default;
throws(throws&&) { throw 42; }
throws& operator=(throws&&) = default;
};
std::variant<std::monostate, throws, int> v{};
expect(not v.valueless_by_exception() and 0_i == v.index());
try {
v = std::move(throws{});
} catch(...) { }
expect(v.valueless_by_exception());
expect(std::variant_npos == v.index());
};
};