3

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?

JeJo
  • 30,635
  • 6
  • 49
  • 88
LunaticJape
  • 1,446
  • 4
  • 21
  • 39

1 Answers1

5

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.

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Actually, I'm comparing fields of two classes, so there will be different type A. Is it still the best approach to do it? – LunaticJape Mar 03 '23 at 06:44
  • Really appreciated. I'm still leaning C++, would you mind explaining the logic of your code? Or why mine does not work? Thanks. – LunaticJape Mar 03 '23 at 07:09