Benjy Kessler
Benjy Kessler

Reputation: 7616

Specialization of non-type template argument

I have a struct

template <auto& t>
struct Foo {
  using Type = decltype(t);
};

I also have a template class:

template <typename T> class MyClass {};

I want to create a specialization for this struct for any arg of type MyClass:

template <typename T>
struct Foo <MyClass<T>& t> {
  using Type = int;
};

I'd Like to be able to use this class like:

Foo<true>::Type t = false;

This code doesn't compile. How can I do this kind of specialization? Is there some other approach using std::enable_if that I can use to accomplish this?

You can see the code at https://onlinegdb.com/1Qzum1Fs2J

Upvotes: 0

Views: 329

Answers (2)

Klaus
Klaus

Reputation: 25613

Your code is near by the needed solution. The specialization simply needs a bit different syntax:

template <typename T> class MyClass {};

template < auto value >
struct Foo  
{
    void Check() { std::cout << "Default" << std::endl; }
};

template <typename T, MyClass<T> value>
struct Foo<value>
{
    void Check() { std::cout << "Spezial" << std::endl; }
};

int main()
{
    Foo<10> fi;
    Foo<MyClass<int>{}> fm;
    fi.Check();
    fm.Check();
}

For gcc it needs trunk version. gcc 11 compiles but delivers wrong result!

See it working: Works on gcc trunk, clang trunk and msvc v19.30

Upvotes: 2

dfrib
dfrib

Reputation: 73186

As per C++20 and P0732R2 (Class Types in Non-Type Template Parameters) non-type template parameters may be of class type, allowing you to use partial specialization to specialize over non-type template arguments that are more specialized than the "any non-type template" argument of the primary template.

#include <type_traits>

// some class types
template<typename T> struct S{};
                     struct U {};

template<auto> // _any_ non-type template parameter
struct is_using_primary_template : std::true_type {};

// partial specialization for specialization of a class template type.
template<typename T, S<T> s>
struct is_using_primary_template<s> : std::false_type {};

// explicit specialization for some class type values.
constexpr S<void> sv{};
constexpr U u{};

template<>
struct is_using_primary_template<sv> : std::false_type {}; 

template<>
struct is_using_primary_template<u> : std::false_type {};

template<auto v>
constexpr bool is_using_primary_template_v{is_using_primary_template<v>::value};

static_assert(is_using_primary_template_v<0>);
static_assert(!is_using_primary_template_v<S<int>{}>);  // partial spec.
static_assert(!is_using_primary_template_v<S<char>{}>); // partial spec.
static_assert(!is_using_primary_template_v<S<void>{}>); // explicit spec.
static_assert(!is_using_primary_template_v<sv>);        // explicit spec.
static_assert(!is_using_primary_template_v<u>);         // explicit spec.

Note that this is not any special case of partial specialization, but just typical use cases where the minimum requirement that all template parameters of each partial specialization are deducible is fulfilled.

That GCC 11 rejects this code is a bug (Bug 99699), which seems to have been (silently or via another bug report?) fixed in GCC 12/trunk. See the Q&A Partial specialization of templates over non-type literal parameters in C++20: clang and gcc disagree for details.

For how to apply this to your particular example, see @Klaus' answer

Upvotes: 0

Related Questions