Search code examples
c++templatesmetaprogrammingvariant

Same template class specialization for std::variant and boost::variant template types


I want to create a class specialization that has the same implementation if it gets passed any std::variant or any boost::variant. I tried to play around with std::enable_if, std::disjunction and std::is_same but I couldn't make it compile. Here is a code sample to show what I want to achieve.

#include <variant>
#include <iostream>
#include <boost/variant.hpp>

template <typename T>
struct TypeChecker;

template <typename T>
struct TypeChecker<T>
{
    void operator()()
    {
        std::cout << "I am other type\n";
    }
}

template <typename ... Ts>  // I want to be able to capture Ts... in TypeChecker scope
struct TypeChecker<std::variant<Ts...> or boost::variant<Ts...>> // what to insert here?
{
    void operator()()
    {
        std::cout << "I am either std::variant or boost::variant\n";
    }
}

int main()
{
    TypeChecker<std::variant<int, float>>{}();
    TypeChecker<boost::variant<int, float>>{}();
    TypeChecker<int>{}();
}

Expected result:

I am either std::variant or boost::variant
I am either std::variant or boost::variant
I am other type

Solution

  • You can use a template template parameter e.g. like this

    template <typename T>
    struct TypeChecker {
        void operator()() {
            std::cout << "I am other type\n";
        }
    };
    
    template<typename ... Ts, template<typename...> typename V>
    requires std::same_as<V<Ts...>, std::variant<Ts...>> ||
             std::same_as<V<Ts...>, boost::variant<Ts...>>
    struct TypeChecker<V<Ts...>>
    {
        void operator()()
        {
            std::cout << "I am either std::variant or boost::variant\n";
        }
    };
    

    Note that this uses C++20 constraints. If you can't use C++20 you can use std::enable_if instead like e.g. this:

    template<typename ... Ts, template<typename...> typename V>
    struct TypeChecker<V<Ts...>> : std::enable_if_t<
            std::is_same_v<V<Ts...>, std::variant<Ts...>> ||
            std::is_same_v<V<Ts...>, boost::variant<Ts...>>, std::true_type>
    {
        void operator()()
        {
            std::cout << "I am either std::variant or boost::variant\n";
        }
    };
    

    You can see it live on godbolt.