2
\$\begingroup\$

I've been fiddling with templates and compile time stuff i implemented a variant container and wanted some opinions and recommendations this is just a learning exercise, a couple template classes not included here are tl_type_at which gives the type in a typelist at a given index; and tl_index_of which gives the index of the first occurrence of the given type in a typelist:

template <size_t Index, typename T, typename ... Ts>
struct variant_deleter
{
    variant_deleter<Index + 1, Ts...> next;
    void operator()(size_t HeldIndex, void* HeldPtr) const
    {
        if(Index == HeldIndex)
            delete static_cast<T*>(HeldPtr);
        else
            next(HeldIndex, HeldPtr);
    }
};

template <size_t Index, typename T>
struct variant_deleter<Index, T>
{
    void operator()(size_t HeldIndex, void* HeldPtr) const
    {
        if(Index == HeldIndex)
            delete static_cast<T*>(HeldPtr);
        }
};

struct bad_variant_access {};

template <typename T, typename ... Ts>
struct variant
{
    typedef typelist<T, Ts...> types;

    variant()
        : union_(new T())
        , held_(0)
    {}
    template <typename I>
    variant(I Init)
        : union_(new typename tl_type_at< tl_index_of<I, types>::index, types >::type())
        , held_(tl_index_of<I, types>::index)
    {}
    ~variant()
    { variant<T, Ts...>::deleter_(held_, union_); }

    template <typename P>
    variant<T, Ts...>& operator=(const P& rhs)
    {
        union_ = new(union_) P(rhs);
        held_ = tl_index_of<P, types>::index;
        return *this;
    }

    template <typename G>
    G& get()
    {
        if(held_ == tl_index_of<G, types>::index)
            return *static_cast<G*>(union_);
        else
            throw bad_variant_access {};
    }
private:
    void* union_;
    size_t held_;
    static const variant_deleter<0, T, Ts...> deleter_;
};
\$\endgroup\$
  • \$\begingroup\$ As explained in the help center, please do not modify your code, because it invalidates existing answers. If you want your updated code to be reviewed you can post a new question for it. \$\endgroup\$ – Pieter Witvoet Sep 14 '17 at 8:17
2
\$\begingroup\$

Some issues:

  • the assignment operator doesn't call the destructor of the existing data, before setting a new value (results in UB, likely a resource leak).

  • by default you do not instantiate an empty variant with the default constructor; the first type in the union may not even be default-constructible.

  • bad_variant_access should probably inherit from a standard exception (std::runtime_error or std::logic_error are common as base classes).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.