std::variant
Overview
Aleksei Ivanov have a look at the example of the interview question that he got asked during the interview loop with the high-frequency trading company.
#include <cstddef>
#include <cstdio>
#include <new>
#include <string>
#include <type_traits>
#include <utility>
template <typename... Types> struct FirstType;
template <typename Head, typename... Tail> struct FirstType<Head, Tail...> {
using Type = Head;
};
template <typename T, typename... Types> struct TypeIndex;
template <typename T, typename... Types> struct TypeIndex<T, T, Types...> {
static constexpr std::size_t Value = 0;
};
template <typename T, typename Head, typename... Tail>
struct TypeIndex<T, Head, Tail...> {
static constexpr std::size_t Value = 1 + TypeIndex<T, Tail...>::Value;
};
template <typename Type, typename... Types> union VariantStorage {
Type Value;
VariantStorage<Types...> Rest;
template <typename Other>
VariantStorage(std::integral_constant<std::size_t, 0>, Other &&Input)
: Value(std::forward<Other>(Input)) {}
template <std::size_t Index, typename Other>
VariantStorage(std::integral_constant<std::size_t, Index>, Other &&Input)
: Rest(std::integral_constant<std::size_t, Index - 1>{},
std::forward<Other>(Input)) {}
template <std::size_t Index, typename Other> void construct(Other &&Input) {
if constexpr (Index == 0) {
new (&Value) Type(std::forward<Other>(Input));
} else {
new (&Rest) VariantStorage<Types...>(
std::integral_constant<std::size_t, Index - 1>{},
std::forward<Other>(Input));
}
}
template <std::size_t Index> auto &get() {
if constexpr (Index == 0) {
return Value;
} else {
return Rest.template get<Index - 1>();
}
}
void destroy(std::size_t ActiveIndex) {
if (ActiveIndex == 0) {
Value.~Type();
return;
}
Rest.destroy(ActiveIndex - 1);
}
~VariantStorage() {}
};
template <typename Type> union VariantStorage<Type> {
Type Value;
template <typename Other>
VariantStorage(std::integral_constant<std::size_t, 0>, Other &&Input)
: Value(std::forward<Other>(Input)) {}
template <std::size_t Index, typename Other> void construct(Other &&Input) {
static_assert(Index == 0, "Index is out of range");
new (&Value) Type(std::forward<Other>(Input));
}
template <std::size_t Index> auto &get() {
static_assert(Index == 0, "Index is out of range");
return Value;
}
void destroy(std::size_t ActiveIndex) {
(void)ActiveIndex;
Value.~Type();
}
~VariantStorage() {}
};
template <typename... Types> class Variant {
public:
using FirstStoredType = typename FirstType<Types...>::Type;
Variant()
: ActiveIndex(0),
Storage(std::integral_constant<std::size_t, 0>{},
FirstStoredType{}) {}
template <typename Type>
Variant(Type &&Input)
: ActiveIndex(TypeIndex<std::decay_t<Type>, Types...>::Value),
Storage(std::integral_constant<
std::size_t,
TypeIndex<std::decay_t<Type>, Types...>::Value>{},
std::forward<Type>(Input)) {}
~Variant() { Storage.destroy(ActiveIndex); }
template <std::size_t Index> auto &get() {
return Storage.template get<Index>();
}
template <typename Type> Variant &operator=(Type &&Input) {
using CleanType = std::decay_t<Type>;
constexpr std::size_t NewIndex = TypeIndex<CleanType, Types...>::Value;
Storage.destroy(ActiveIndex);
Storage.template construct<NewIndex>(std::forward<Type>(Input));
ActiveIndex = NewIndex;
return *this;
}
private:
std::size_t ActiveIndex;
VariantStorage<Types...> Storage;
};
int main() {
Variant<int, std::string, std::size_t> Value;
Value = std::string("Hello");
std::printf("str = %s\n", Value.get<1>().c_str());
Value = std::size_t(1000);
std::printf("size_t = %zu\n", Value.get<2>());
Value = int(2000);
std::printf("int = %d\n", Value.get<0>());
}
Leave a comment