c++templatesc++17variant

Deduce template parameters for std::variant template parameter


I have a template class that takes std::variant as a template parameter:

template<class T>
class X
{
};

X<std::variant<int, float>> x;

I want to add a constraint for T to be derived from std::variant. Then I want to extract template parameters of this variant to use them inside the class. Like this:

template<class T, std::enable_if_t<
    std::is_base_of_v<std::variant<T::_Types>, T>, // _Types = int, float
bool> = true>
class X
{
    std::tuple<T::_Types> t; // std::tuple<int, float>
};

X<std::variant<int, float>> x;

The problem is that I cannot get these int, float parameters from the variant type. Is there a way to do this in C++17?

This variant comes from return type of a function. Because of this I cannot change it for something like variant parameters instead (to take int, float directly).


Solution

  • The types_list type traits provides a specialized tuple for the different types that constitute the specialization of the template class T, otherwise provides an empty tuple. For simplicity, it is used the standard std::tuple.

    To implement the technique, it is sufficient to add a partial specialization that accepts a generic class and its list of template arguments.

    Example:

    template <typename>
    struct types_list
    { using type = std::tuple<>; };
    
    template <template <typename...> typename T, typename ...Args>
    struct types_list<T<Args...>>
    { using type = std::tuple<Args...>; };
    
    template <typename T>
    using types_list_t = typename types_list<T>::type;
    

    On the other hand, the is_variant type trait verifies whether the type T is a specialization of std::variant.

    To implement the condition, it is sufficient to add a partial specialization that accepts a std::variant specialization.

    Example:

    template <typename>
    struct is_variant
     : std::false_type {};
    
    template <typename ...Args>
    struct is_variant<std::variant<Args...>>
     : std::true_type {};
    
    template <typename T>
    inline constexpr bool is_variant_v = is_variant<T>::value;
    

    Thus, the class can be re-implemented in the following way.

    template <typename T, typename = std::enable_if_t<is_variant_v<T>>>
    class X
    {
      types_list_t<T> t;
    };