Search code examples
c++c++20

Cannot substitute template argument for template template parameter


I have a templated struct like this:

template<int Min, int Max>
struct TInt
{
    int Get() const { return StoredVal; }
    TInt& operator=(int Val) { StoredVal = FMath::Clamp(Val, Min, Max); return *this; }
    bool WithinRange(int Val) const { return Min <= Val && Val <= Max; }

private:
    int StoredVal = Min;
};

And after reading this proposal tried to create these templates to check if a type is a template specialization of TInt:

template<class T, template<class...> class Primary>
struct is_specialization_of : std::false_type {};

template<template<class...> class Primary, class... Args>
struct is_specialization_of<Primary<Args...>, Primary> : std::true_type {};

template<class T, template<class...> class Primary>
inline constexpr bool is_specialization_of_v = is_specialization_of<T, Primary>::value;

This is how I try to use it:

template<typename T>
struct IsValidType { static constexpr bool Value = is_specialization_of<T, TInt>; };

But I keep getting this error: "Cannot substitute template argument TInt for template template parameter Primary". I have checked and tried other answers to similar questions here on SO, but nothing really seems to work for me.

I don't really care about the general case (variadic template), just two template parameters which would also increase my learning if I could do exactly that.

What do I need to do to make this work?


Solution

  • The problem is:

    template<class T, template<class...> class Primary>
    struct is_specialization_of : std::false_type {};
    

    is_specialization_of takes something of the form template <class...> class Primary. A class template that accepts some number of type template parameters. But TInt doesn't accept type template parameters, it accepts non-type template parameters (specifically two ints). There's no way in C++ today to write is_specialization_of properly.


    But if you just want to write a specific trait to check if something is a TInt, that's much more straightforward with a variable template:

    template <class T>
    inline constexpr bool is_tint = false;
    
    template <int Min, int Max>
    inline constexpr bool is_tint<TInt<Min, Max>> = true;
    

    or shoved onto "one line" (ish) with a requires expression:

    template <class T>
    concept is_tint = requires (T const& t){
        []<int Min, int Max>(TInt<Min, Max> const&){}(t);
    };