Partial Specialisation of a Template Template Parameter

Hi,

I've been trying to create a templated class that takes a template as a parameter. I'd like to specialise this class for certain partial specialisations of the template parameter but can't seem to figure out how to do it nor find anything online, (although I may be searching for the wrong thing).

As an example, say I have a class A that takes a template class with two parameters as its parameter:

template< template<class X, class Y> class Z > class A {};

I'd like to have a general version of A, for a general version of Z, but a specialisation of A for a specialisation of Z, e.g. where X is int but Y is still any type.

Could anyone tell me please, is this possible?

Thanks.
> template< template<class X, class Y> class Z > class A {};
> I'd like to have a general version of A, for a general version of Z,
> but a specialisation of A for a specialisation of Z, e.g. where X is int but Y is still any type.

Add the arguments required as parameters to instantiate the template template parameter.
And then specialize the template as would be done normally.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>

template < typename T, typename U, template<typename,typename> class C >
struct S { S() { std::cout << "S<T,U,C> generalization: " ; C<T,U> cc ; } };

template < typename T, typename U, template<typename,typename> class C >
struct S< T*, U, C >
{ S() { std::cout << "S<T*,U,C> partial specialization: " ; const C<T*,U> cc ; } };

template < typename T, typename U, template<typename,typename> class C >
struct S< T*, U*, C >
{ S() { std::cout << "S<T*,U*,C> partial specialization: " ; C<T*,U*> cc ; } };

template < typename U, template<typename,typename> class C >
struct S< int, U, C >
{ S() { std::cout << "S<int,U,C> partial specialization: " ; const C<short,U> cc ; } };

template < template<typename,typename> class C >
struct S< int, int, C >
{ S() { std::cout << "S<int,int,C> partial specialization: " ; C<short,short> cc ; } };

template < typename, typename > struct A
{ A() { std::cout << "generalization A<any,any>\n" ; } };

template < typename T, typename U > struct A<T*,U>
{ A() { std::cout << "partial specialization A<pointer,any>\n" ; } };

template < typename T, typename U > struct A<T*,U*>
{ A() { std::cout << "partial specialization A<pointer,pointer>\n" ; } };

template < typename U > struct A<short,U>
{ A() { std::cout << "partial specialization A<short,any>\n" ; } };

template <> struct A<short,short>
{ A() { std::cout << "complete specialization A<short,short>\n" ; } };

int main()
{
   S< char, double, A > a ;
   // S<T,U,C> generalization: generalization A<any,any>

   S< char*, double, A > b ;
   // S<T*,U,C> partial specialization: partial specialization A<pointer,any>

   S< char*, double*, A > c ;
   // S<T*,U*,C> partial specialization: partial specialization A<pointer,pointer>

   S< int, double, A > d ;
   // S<int,U,C> partial specialization: partial specialization A<short,any>

   S< int, int, A > e ;
   // S<int,int,C> partial specialization: complete specialization A<short,short>
}

http://coliru.stacked-crooked.com/a/1d0adce89ffc1f70
Thanks JLBorges, I think by using your post and this link (which I found totally by accident - such is the way with stackoverflow!)

http://stackoverflow.com/questions/4189945/templated-class-specialization-where-template-argument-is-a-template

I've come up with a solution that seems to work so far. In my first go,
1
2
3
4
5
6
7
template <class Traits> class A{ ... }; //Generalisation, not intended for use

template <> template<bool b1, bool b2, bool b3, bool b4, bool b5, bool b6>
class A<TypeTraits<b1,b2,b3,b4,b5,b6> > { ... }; //Specialisation for any type traits

template <> template<bool b1, bool b2, bool b5, bool b6>
struct A<TypeTraits<b1,b2,true, true,b5,b6> > { ... }; //Specialisation for certain type traits 

This worked as it is, but when I added another type it didn't:
1
2
template<class T> template<bool b1, bool b2, bool b5, bool b6>
class A<T, TypeTraits<b1,b2,true, true,b5,b6> > { ... }; // Once class T added, this specialisation not chosen. 

, however when I change it to this, it seems to work fine:
1
2
template<class T, bool b1, bool b2, bool b5, bool b6>
class A<T, TypeTraits<b1,b2,true, true,b5,b6> > { ... }; // Now specialisations chosen correctly (so far!!!) 
> This worked as it is, but when I added another type it didn't
1
2
> template<class T> template<bool b1, bool b2, bool b5, bool b6>
> class A<T, TypeTraits<b1,b2,true, true,b5,b6> > { /*...*/ }; 

> however when I change it to this, it seems to work fine:
1
2
> template<class T, bool b1, bool b2, bool b5, bool b6>
> class A<T, TypeTraits<b1,b2,true, true,b5,b6> > { /*... */ };


AFAIK, with this generalisation which has just one template type parameter:
template <class Traits> class A{ ... }; //Generalisation, not intended for use

neither of those specializations should work; both are trying to specialize A with two (instead of one) template type parameters.


It is not very clear to me what your design intent is; are you looking for something like this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

template < bool... FLAGS > struct traits { /* ... */ } ;

// generalisation, not intended for use, so not defined
template < typename... ARGS > struct A ;

// specialisation for traits with any 6 bools
template < bool b1, bool b2, bool b3, bool b4, bool b5, bool b6 >
struct A < traits<b1,b2,b3,b4,b5,b6> > { static constexpr int tag = 1 ; };

// specialisation for type traits <any,any,true,true,any,any>
template < bool b1, bool b2, bool b5, bool b6 >
struct A < traits<b1,b2,true, true,b5,b6> > { static constexpr int tag = 2 ; };

// specialisation for any T, type traits <any,any,true,true,any,any>
template < typename T, bool b1, bool b2, bool b5, bool b6>
struct A< T, traits<b1,b2,true, true,b5,b6> > { static constexpr int tag = 3 ; };

// specialisation for int, any pointer, type traits <any,any,true,true,false,false>
template < typename T, bool b1, bool b2, bool b5, bool b6>
struct A< int, T*, traits<b1,b2,true, true,b5,b6> > { static constexpr int tag = 4 ; };

int main()
{
    std::cout << A< traits<1,0,1,0,1,0> >::tag << '\n' ; // 1
    std::cout << A< traits<1,0,1,1,1,0> >::tag << '\n' ; // 2
    std::cout << A< void, traits<1,0,1,1,1,0> >::tag << '\n' ; // 3
    std::cout << A< int, double*, traits<1,0,1,1,0,0> >::tag << '\n' ; // 4
}

http://coliru.stacked-crooked.com/a/d2532bf0c668129d
That's pretty what I'm trying to do, except that A should always take two template parameters, one type, and a set of traits. So far I've grouped the traits into a templated struct, and written a convenience template that takes a type and contains a nested type, where this type is the traits structure for that type. Therefore, the generalised version is this:
1
2
3
4
template <class =T, class Traits = TTraits<T>::Type > class A; //Generalisation, compiler won't find definition

template<class T, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6>
class A<T, TypeTraits<b1,b2,b3,b4,b5,b6> > { ... }; 



I then would like to have specialised versions for certain type traits combinations, eg
1
2
template<class T, bool b1, bool b2, bool b4, bool b6>
class A<T, TypeTraits<b1,b2,true,b4,false,b6> > { ... }; //  


Therefore, for example, an A<int>, an A<MyClass> and an A<MyPolymorphicClass> should all be different, depending on how the traits structure is setup and how A is specialised.



> template <class =T, class Traits = TTraits<T>::Type > class A;

Need to use typename to disambiguate that the dependant name is the name of a type.
template < class T, class Traits = typename TTraits<T>::Type > class A;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>

template < typename T > struct traits_of ;

template < bool... FLAGS > struct traits { /* ... */ } ;

template <> struct traits_of<int>
{ using type = traits<true,false,true,false,true,false> ; } ;

template <> struct traits_of<double> { using type = traits<true,false,true> ; } ;

// A should always take two template parameters, one type, and a set of traits.
// generalisation, not intended for use, so not defined
template < typename T, typename TRAITS = typename traits_of<T>::type > struct A ;

// specialisation for int, traits with any 6 bools
template < bool b1, bool b2, bool b3, bool b4, bool b5, bool b6 >
struct A < int, traits<b1,b2,b3,b4,b5,b6> > { static constexpr int tag = 1 ; };

// specialisation for int, traits<any,any,true,true,any,any>
template < bool b1, bool b2, bool b5, bool b6 >
struct A < int, traits<b1,b2,true, true,b5,b6> > { static constexpr int tag = 2 ; };

// specialisation for double, traits<any,any,any>
template < bool b1, bool b2, bool b3 >
struct A< double, traits<b1,b2,b3> > { static constexpr int tag = 3 ; };

// specialisation for double, traits<true,any,false>
template < bool b2 >
struct A< double, traits<true,b2,false> > { static constexpr int tag = 4 ; };

int main()
{
    std::cout << A<int>::tag << '\n' ; // 1
    std::cout << A< int, traits<1,0,1,0,1,0> >::tag << '\n' ; // 1
    std::cout << A< int, traits<1,0,1,1,1,0> >::tag << '\n' ; // 2

    std::cout << A<double>::tag << '\n' ; // 3
    std::cout << A< double, traits<0,0,0> >::tag << '\n' ; // 3
    std::cout << A< double, traits<1,0,0> >::tag << '\n' ; // 4
}

http://coliru.stacked-crooked.com/a/724ebcda00d730bd
Last edited on
Need to use typename to disambiguate that the dependant name is the name of a type.
template < class T, class Traits = typename TTraits<T>::Type > class A;

Thanks, I mistyped that as I didn't have my code with me at that time.
Topic archived. No new replies allowed.