Search code examples
c++templatestype-traits

std::is_same_v with unspecialized template


I have the following code:

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T, int N>
class A {
public:
    static constexpr int n = N;
};

template<typename T, typename X, int N>
class B {
public:
    static constexpr int x = N;
};

template<typename T>
void test(T) {
    if constexpr (std::is_same_v<T, A>) {
        int a = T::n;
    } else if constexpr (std::is_same_v<T, B>) {
        int a = T::x;
    }
    cout << "a";
};

int main()
{
    A<int, 2> a;
    test(a);
    return 0;
}

Compiling it produces the following error:

error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Tp, class _Up> constexpr const bool std::is_same_v<_Tp, _Up>’
   20 |     if constexpr (std::is_same_v<T, A>) {
      |                   ~~~~~^~~~~~~~~~~~~~~
note:   expected a type, got ‘A’

The Problem is that I cannot use a correct type here (like A), since I don't know what the template parameters are or how many there are. Basically I want to match any type of class A with any template arguments.


Solution

  • You need to write your own trait for that:

    template<typename>
    struct is_specialization_of_A : std::false_type {};
    
    template<typename T, int N>
    struct is_specialization_of_A<A<T,N>> : std::true_type {};
    
    template<typename T>
    inline constexpr auto is_specialization_of_A_v = is_specialization_of_A<T>::value;
    

    and analogously for B.

    You could take the template as template template parameter of the trait as well, so that you only need one trait definition, but that only works as long as the template parameter categories of the templates match. This is not the case here (A has <type, non-type>, while B has <type, type, non-type>).

    template<typename, template<typename, auto> class>
    struct is_specialization_of : std::false_type {};
    
    template<template<typename, auto> class Tmpl, typename T, auto N>
    struct is_specialization_of<Tmpl<T, N>, Tmpl> : std::true_type {};
    
    template<typename T, template<typename, auto> class Tmpl>
    inline constexpr auto is_specialization_of_v = is_specialization_of<Tmpl, T>::value;
    

    This can be used as is_specialization_of_v<A, T>, but not is_specialization_of_v<B, T>.