#include #include #include struct Either_Error : public std::runtime_error { using std::runtime_error::runtime_error; }; template class Either { public: Either(T1 const& first) : first { first }, is_first { true } { } Either(T2 const& second) : second { second }, is_first { false } { } ~Either() { if (is_first) first.~T1(); else second.~T2(); } Either& operator=(T1 const& other) { if (is_first) { first = other; } else { second.~T2(); new (&first) T1 { other }; is_first = true; } return *this; } Either& operator=(T2 const& other) { if (is_first) { first.~T1(); new (&second) T2 { other }; is_first = false; } else { second = other; } return *this; } bool has_first() const { return is_first; } T1& get_first() { if (is_first) { return first; } throw Either_Error { "Invalid type" }; } T2& get_second() { if (is_first) { throw Either_Error { "Invalid type" }; } return second; } private: union { T1 first; T2 second; }; bool is_first; }; int main() { // test basic functionality { Either> e { "hello" }; assert( e.has_first() ); assert( e.get_first() == "hello" ); e = "a kinda long string to make sure memory is allocated"; assert( e.get_first() == "a kinda long string to make sure memory is allocated" ); // Make sure the invalid version throws an exception try { e.get_second(); assert(false); } catch (Either_Error&) { } e = std::vector { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; assert( !e.has_first() ); assert(( e.get_second() == std::vector { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } )); // Make sure the invalid version throws an exception try { e.get_first(); assert(false); } catch (Either_Error&) { assert(true); } e = "Another string"; assert( e.has_first() ); assert( e.get_first() == "Another string" ); } // test destructors { std::vector v { 1, 2, 3, 4, 5, 6, 7, 8 }; Either, std::string> e1 { v }; assert( e1.get_first() == v ); std::string s { "a longer string which must take some space, thus ignoring short strings" }; Either, std::string> e2 { s }; assert( e2.get_second() == s ); } }