#ifndef VARIANT_H_ #define VARIANT_H_ #include #include #include #include #include template T& as(char* memory) { return *std::launder(reinterpret_cast(memory)); } template struct Variant_Helper; template struct Variant_Helper { static constexpr std::size_t const size { std::max(sizeof(Type), Variant_Helper::size) }; template static constexpr int index_of() { if constexpr (std::is_same_v) return N; else return Variant_Helper::template index_of(); } static void destroy(char* memory, int type) { if (type == 0) { as(memory).~Type(); } else { Variant_Helper::destroy(memory, type - 1); } } }; template <> struct Variant_Helper<> { static constexpr std::size_t const size { 0 }; template static constexpr int index_of() { return -1; } static void destroy(char*, int) { throw std::bad_cast{}; } }; template class Variant { private: using Helper = Variant_Helper; char memory[ Helper::size ]; int type { -1 }; public: template >> Variant(T&& value) // Note: Helper is a dependent name (it depends on Types...) so the compiler will // assume that index_of is *not* a template. To fix this we have to add the template // keyword before index_of just to communicate to the compiler that it actually is // a template (See seminar 6 for details). : type { Helper::template index_of() } { new (&memory[0]) NormalizedType { std::forward(value) }; } template T& get() { if (Helper::template index_of() == type) return as(&memory[0]); else throw std::bad_cast{}; } template >> Variant& operator=(T&& value) { if (Helper::template index_of() == type) { as(&memory[0]) = std::forward(value); } else { Helper::destroy(&memory[0], type); new (&memory[0]) NormalizedType { std::forward(value) }; type = Helper::template index_of(); } return *this; } }; #endif