Search code examples
c++templatestype-traits

How to tell if a type is an instance of a specific template class?


I have a function that takes a template type to determine a return value. Is there any way to tell at compile time if the template type is some instantiation of a template class?

Ex.

class First { /* ... */ };

template <typename T>
class Second { /* ... */ };

using MyType = boost::variant<First, Second<int>, Second<float>>;

template <typename SecondType>
auto func() -> MyType {
    static_assert(/* what goes here?? */, "func() expects Second type");
    SecondType obj;
    // ...
    return obj;
}

MyType obj = func<Second<int>>();

I know it is possible to get around this by doing

template <typename T>
auto func() -> MyType {
    static_assert(std::is_same<T, int>::value || std::is_same<T, float>::value,
                  "func template must be type int or float");

    Second<T> obj;
    // ...
    return obj;
}

MyType obj = func<int>();

I'm just curious in general if there is a way to test if a type is an instantiation of a template class? Because if MyType ends up having 6 Second instantiations, I don't want to have to test for all possible types.


Solution

  • Here's an option:

    #include <iostream>
    #include <type_traits>
    #include <string>
    
    template <class, template <class> class>
    struct is_instance : public std::false_type {};
    
    template <class T, template <class> class U>
    struct is_instance<U<T>, U> : public std::true_type {};
    
    template <class>
    class Second 
    {};
    
    int main()
    {
        using A = Second<int>;
        using B = Second<std::string>;
        using C = float;
        std::cout << is_instance<A, Second>{} << '\n'; // prints 1
        std::cout << is_instance<B, Second>{} << '\n'; // prints 1
        std::cout << is_instance<C, Second>{} << '\n'; // prints 0
    }
    

    It's basically specializing the is_instance struct for types that are instantiations of a template.