Skip to content

Latest commit

 

History

History
327 lines (260 loc) · 9.72 KB

227.md

File metadata and controls

327 lines (260 loc) · 9.72 KB
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

    • https://eel.is/c++draft/variant

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());
}

https://godbolt.org/z/PnKaY4Kq5

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());
      };
  };
}

https://godbolt.org/z/z6G7jEEno

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());
    };
};

https://godbolt.org/z/avvG5ob1W

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());
    };
};

https://godbolt.org/z/47Y389jcf

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());
    };
};

https://godbolt.org/z/6Gqx3Edvr

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());
    };
};

https://godbolt.org/z/KvW8jeoGz

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());
    };
};

https://godbolt.org/z/M7hGndsrM

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());
    };
};

https://godbolt.org/z/eff6Wdd7P