c++templatestype-traitstypecheckingspecialization

How to check type equality between non-template class and template class?


Suppose I have

class A {};

template<typename T, typename U> class Wrapper {};

I'm trying to check if the wrapper's first inner type is A.

typeCheck(new A(), new Wrapper<A,B>);  // expect true
typeCheck(new A(), new Wrapper<C,B>);  // expect false

What's the best approach to do it?


I've tried template partial specialization but did not have luck.

Here is the code I tried:

template<typename T> struct get_inner {
  using type = T;
}

template<typename T, typename U>
struct get_inner<Wrapper<T,U>> {
  using type = T;
}

std::cout<< typeid(get_inner<decltype(new Wrapper<A,B>)>::type);

The console shows the same wrapper type instead of the inner type A. Is there anything mistake here?


Solution

  • The console shows the same wrapper type instead of the inner type A. Is there anything mistake here?

    The type of new Wrapper<A,B> is a pointer to the Wrapper<A,B>, not Wrapper itself. Therefore, it doesn't suit to your specialization, and chose the first base template, where type is equal to the passed type, which is Wrapper<A,B>*.

    Instead, you should have written

    typename get_inner<Wrapper<A,B>>::type; 
    

    Note that, the decltype is also unnecessary because you are passing the template type itself already.

    Since you are trying to see check the type at compile time, I would suggest using std::is_same as well. For example

    std::cout << std::boolalpha
              << std::is_same_v<typename get_inner<Wrapper<A,B>>::type, A>;
    

    See a demo


    What's the best approach to do it?

    Well, there are many ways!

    One way is to do, like std::is_same_v, in standard type traits, you can write traits for this directly:

    #include <type_traits>  // std::false_type, std::true_type
    
    template<typename T> // base template
    struct is_inner_type : std::false_type{};
    
    template<typename T, typename U> // specialization
    struct is_inner_type<Wrapper<T,U>> {
      static constexpr bool value = std::is_same_v<A, T>;
    };
    // variable template for convenience
    template<typename ClassType>
    inline constexpr bool is_inner_type_v = is_inner_type<ClassType>::value;
    

    And you would use it like

    static_assert(!is_inner_type_v<Wrapper<int, float>>);
    static_assert(is_inner_type_v<Wrapper<A, float>>);
    

    See a demo

    However, that is not generic enough to change the type to be checked. There, I would suggest something similar to your original implementation.

    // traits for finding the T & U type!
    template <typename T> struct Class;
    template <template<typename A, typename B> class Type,  typename A, typename B>
    struct Class<Type<A,B>> {
        using T = A;
        // using U = B; // if needed
    };
    template <typename Type> using  class_t = typename Class<Type>::T;
    // template <typename Type> using  class_u = typename Class<Type>::U; // if needed
    
    // variable template for convenience
    template<typename ClassType, typename TypeToCheck>
    inline constexpr bool is_inner_type_v = std::is_same_v<class_t<ClassType>, TypeToCheck>;
    

    I used, template template class specialization, in case you want to make it more generic and use it for other similar class templates.