Olzhas Zhumabek Olzhas Zhumabek - 3 months ago 20
C++ Question

How to write std::variant

My aim is to write

std::variant
, may be not full blown, but at least with fully working constructor/destructor pair and
std::get<>()
function.

I tried to reserve a memory using char array. The size of it is determined by the biggest type, which is found by using
find_biggest_size<>()
function. The constructor uses static assert, because it performs check if the type is in the list of specified types. For now, the constructor and in place constructor works.

template <typename ... alternatives>
class variant
{
char object[find_biggest_size<alternatives...>::value];
public:
template <typename T>
variant(T&& other)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<T>(other));
}

template <typename T, typename ... ArgTypes>
variant(in_place_t<T>, ArgTypes&& ... args)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<ArgTypes>(args)...);
}

~variant()
{
// what to do here?
}
};


Then I've stumbled upon a problem. I don't know what destructor to execute when the object dies. On top of that, it is impossible to access the underlying object, since I can't specialize
std::get<>()
to get the right type.

My question is: how to store the type after the creation of the object? Is it the right approach? If not, what should I use?

EDIT:

I tried to apply the comments. The problem is that the index of the type that is currently alive can't be
constexpr
, thus I can't extract the needed type from type list and invoke appropriate destructor.

~variant()
{
using T = typename extract<index, alternatives...>::type;
(T*)&object[0]->~T();
}

Answer

How I'd probably start:

#include <iostream>
#include <utility>
#include <array>

template<class...Types>
struct variant
{
    variant() {}
    ~variant()
    {
        if (type_ >= 0)
        {
            invoke_destructor(type_, reinterpret_cast<char*>(std::addressof(storage_)));
        }
    }

    template<class T> static void invoke_destructor_impl(char* object)
    {
        auto pt = reinterpret_cast<T*>(object);
        pt->~T();
    }

    static void invoke_destructor(int type, char* address)
    {
        static const std::array<void (*)(char*), sizeof...(Types)> destructors
        {
            std::addressof(invoke_destructor_impl<Types>)...
        };
        destructors[type](address);
    }

    std::aligned_union_t<0, Types...> storage_;
    int type_ = -1;

};

int main()
{
    variant<int, std::string> v;

}
Comments