#include template struct get_index_by_type { static const size_t value = -1; }; template struct get_index_by_type { static const size_t value = std::is_same_v ? 0 : get_index_by_type::value + 1; }; template static const size_t get_index_by_type_v = get_index_by_type::value; template struct t_is_on_of_types { static const bool value = false; }; template struct t_is_on_of_types { static const bool value = std::is_same_v || t_is_on_of_types::value; }; template static const bool t_is_on_of_types_v = t_is_on_of_types::value; template struct get_type_by_index {}; template struct get_type_by_index<0, T, Types...> { using value = T; }; template struct get_type_by_index { using value = typename get_type_by_index::value; }; template struct count_types_in_package {}; template struct count_types_in_package { static const size_t value = std::is_same_v + count_types_in_package::value; }; template struct count_types_in_package { static const size_t value = 0; }; template using get_type_by_index_v = typename get_type_by_index::value; template struct get_max_size { static const size_t value = 0; }; template struct get_max_size { static const size_t value = std::max(sizeof(Head), get_max_size::value); }; template static const size_t get_max_size_v = get_max_size::value; template class Variant; template struct VariantAlternative; template struct VariantAlternative { public: VariantAlternative() {} VariantAlternative(const VariantAlternative&) {} VariantAlternative(const T& value) { auto* me = std::launder(static_cast*>(this)); me->index_ = get_index_by_type_v; new(me->object_) T(value); } VariantAlternative(T&& value) { auto* me = std::launder(static_cast*>(this)); me->index_ = get_index_by_type_v; new(me->object_) T(std::move(value)); } VariantAlternative& operator=(const T& value) { auto* me = std::launder(static_cast*>(this)); me->destroy(); me->index_ = get_index_by_type_v; new(me->object_) T(value); return *this; } VariantAlternative& operator=(T&& value) { auto* me = static_cast*>(this); me->destroy(); me->index_ = get_index_by_type_v; new(me->object_) T(std::move(value)); return *this; } template && std::is_constructible_v, bool> = true> VariantAlternative(const K& value) { auto* me = std::launder(static_cast*>(this)); me->index_ = get_index_by_type_v; me->index_ = get_index_by_type_v; new(me->object_) T(value); } template && std::is_constructible_v, bool> = true> VariantAlternative(K&& value) { auto* me = std::launder(static_cast*>(this)); me->index_ = get_index_by_type_v; me->index_ = get_index_by_type_v; new(me->object_) T(std::forward(value)); } template && std::is_assignable_v, bool> = true> VariantAlternative& operator=(const K& value) { auto* me = static_cast*>(this); me->destroy(); me->index_ = get_index_by_type_v; new(me->object_) T(value); return *this; } template && std::is_assignable_v, bool> = true> VariantAlternative& operator=(K&& value) { auto* me = static_cast*>(this); me->destroy(); me->index_ = get_index_by_type_v; new(me->object_) T(std::forward(value)); return *this; } ~VariantAlternative() { auto* me = static_cast*>(this); if (get_index_by_type_v == me->index_) { me->destroy(); } } }; template bool holds_alternative(const Variant& another) { return another.template holds_alternative(); } template const T& get(const Variant& another) { static_assert(t_is_on_of_types_v); static_assert(count_types_in_package::value == 1); if (another.template holds_alternative()) { return *reinterpret_cast(std::launder(const_cast&>(another).object_)); } else { throw std::bad_cast(); } } template T& get(Variant& another) { return const_cast(get(const_cast&>(another))); } template T&& get(Variant&& another) { return std::move(const_cast(get(const_cast&>(another)))); } template const get_type_by_index_v& get(const Variant& another) { return get>(another); } template get_type_by_index_v& get(Variant& another) { using R = get_type_by_index_v; return const_cast(get(const_cast&>(another))); } template get_type_by_index_v&& get(Variant&& another) { using R = get_type_by_index_v; return std::move(const_cast(get(const_cast&>(another)))); } template class Variant : public VariantAlternative... { template friend class VariantAlternative; private: static const size_t size_variant = get_max_size_v; alignas(Types...) int8_t object_[size_variant]; size_t index_; template bool holds_alternative() const { if (get_index_by_type_v == -1) throw std::bad_cast(); else return get_index_by_type_v == index_; } template void destroy() { if (index_ == N) { index_ = -1; using R = get_type_by_index_v; reinterpret_cast(object_)->~R(); } else { if constexpr (N + 1 < sizeof...(Types)) { destroy(); } } } template void copy_constructor(const Variant& another) { if (another.index_ == N) { using R = get_type_by_index_v; new(object_) R(*reinterpret_cast(std::launder(another.object_))); index_ = another.index_; } else { if constexpr (N + 1 < sizeof...(Types)) { copy_constructor(another); } } } template void move_constructor(Variant&& another) { if (this == &another) return; if (another.index_ == N) { using R = get_type_by_index_v; new(object_) R(std::move(*reinterpret_cast(std::launder(another.object_)))); index_ = another.index_; } else { if constexpr (N + 1 < sizeof...(Types)) { move_constructor(std::move(another)); } } } template void swap(Variant& another) { if (index_ == N && another.index_ == M) { using R = get_type_by_index_v; using S = get_type_by_index_v; S tmp(std::move(*reinterpret_cast(another.object_))); new(another.object_) R(std::move(*reinterpret_cast(object_))); new(object_) S(std::move(tmp)); std::swap(index_, another.index_); } else if (index_ != N) { if constexpr(N + 1 < sizeof...(Types)) swap(another); } else { if constexpr(M + 1 < sizeof...(Types)) swap(another); } } public: using VariantAlternative::operator=...; using VariantAlternative::VariantAlternative...; Variant() : index_(0) { new(object_) get_type_by_index_v<0, Types...>(); } Variant(Variant&& another) { move_constructor(std::move(another)); } Variant(const Variant& another) : Variant() { copy_constructor(another); } Variant& operator=(const Variant& another) { Variant tmp(another); swap(tmp); return *this; } Variant& operator=(Variant&& another) noexcept { if (&another == this) return *this; Variant tmp(std::move(another)); swap(tmp); return *this; } template T& emplace(Args&&... args) { destroy(); index_ = get_index_by_type_v; new(object_) T(std::forward(args)...); return *reinterpret_cast(object_); } template get_type_by_index_v& emplace(Args&&... args) { using T = get_type_by_index_v; destroy(); index_ = get_index_by_type_v; new(object_) T(std::forward(args)...); return *reinterpret_cast(object_); } template T& emplace(std::initializer_list il, Args&&... args) { destroy(); index_ = get_index_by_type_v; new(object_) T(il, std::forward(args)...); return *reinterpret_cast(object_); } template get_type_by_index_v& emplace(std::initializer_list il, Args&&... args) { using T = get_type_by_index_v; destroy(); index_ = get_index_by_type_v; new(object_) T(il, std::forward(args)...); return *reinterpret_cast(object_); } size_t index() const { return index_; } bool valueless_by_exception() const { return index_ == -1; } template friend bool holds_alternative(const Variant& another); template friend const T& get(const Variant& another); template friend T& get(Variant& another); template friend T&& get(Variant&& another); template friend const get_type_by_index_v& get(const Variant& another); template friend get_type_by_index_v& get(Variant& another); template friend get_type_by_index_v&& get(Variant&& another); };