Search code examples
c++templatespartial-specializationclass-template

How can I partially specialize class template non-type parameters


My question is pretty basic. I'm trying to give myself a convenience class template instantiation that does something mathematically intuitive. I want to be able to instantiate a class called Q<0,31> by instantiating it as Q<31>. According to cppreference.com, this should be possible:

template<class T> 
class A<int, T*, 5> {}; // #3: partial specialization where T1 is int, I is 5,
                        //     and T2 is a pointer

But when I try this:

template< unsigned integral, unsigned fractional >
class Q: public Signed<integral + fractional + 1u> {};

// There's a shorthand for Q notation where you don't specify the number of integer bits, and it's assumed to be 0
template< unsigned fractional > class  Q<0u, fractional>: public Signed<fractional + 1> {};

static_assert( std::is_same< Q<31>, Q<0,31> >::value, "Partial specialization should be equivalent to no integer component");

then I get an error saying I haven't passed enough template arguments


Solution

  • Specializations are not overloads. They are pattern matching.

    There is no way to "overload" a template like you want, short of magic values.

    template< unsigned fractional >
    class  Q<0u, fractional>
    

    this just pattern matches.

    Your arguments are always

    template< unsigned integral, unsigned fractional >
    class Q
    

    and your specialization matches when someone passes 0u for integral. So it matches

    Q<0u, fractional>
    

    it does not match

    Q<fractional>
    

    now, as mentioned, you can do this with magic values:

    template< unsigned integral, unsigned fractional=-1 >
    class Q
    template< unsigned fractional >
    class Q<fractional, -1>:Q<0u, fractional> {}
    

    but that also happens if someone passes -1 manually.

    In you'll be able to have user defined types as template non-type template parameters, so you could do something like:

    template< unsigned A, optional<unsigned> B = {} >
    class Q;
    
    template< unsigned integral, unsigned fractional >
    class Q< integral, optional<unsigned>(fractional) > // 2 arg case
    
    template< unsigned fractional >
    class Q< fractional, nullopt >:Q<0u, fractional> {}; // 1 arg case
    

    but that isn't here yet.