When specialization of template class is allowed with more template arguments than it’s been declared?

The code is very simple and concise (YATC means yet another template class :))

template<typename T1, typename T2>
class YATC; /*declaration*/

template<typename T1>
class YATC<T1,T1> {}; 

template<typename T1, typename T2, typename T3>
class YATC<T1, YATC<T2, T3>> {};

int main()
{
    YATC<int, YATC<int, double>> yatc;
    return 0;
}

This black magic looks very disturbing for me.

  • I declared the class as a template class with two template arguments T1,T2;

  • I’ve expected that I can make specializations for YATC class based on idea that I can specificate definition of the class on two template arguments only;

  • I’ve found out that actually I can make specialization using infinite template arguments like typename T1, typename T2, typename T3 but with some interesting restrictions:

  • I cannot use std::common_type_t<T1, T2> as second parameter for YATC specialization. The compiler throws an error that T2 isn’t used for YATC‘s specialization;

  • However, I can use them for using specialization with instance of some template class that receives these arguments (as YATC<T2, T3>);

  • But! I still cannot instantiate the class with YATC<int, int, double>, only YATC<int, YATC<int, double>>;

Why the such declaration is still valid but why I cannot instantiate the class with YATC<int, int, double>?

Answer

I’ve expected that I can make specializations for YATC class based on idea that I can specificate definition of the class on two template arguments only;

And this is correct.

I’ve found out that actually I can make specialization using infinite template arguments like typename T1, typename T2, typename T3 but with some interesting restrictions:

Yes… but still remain that YACT has only two template arguments

Take your second specialization

template<typename T1, typename T2, typename T3>
class YATC<T1, YATC<T2, T3>> {};

You use three template parameter but YACT receive only two template arguments:

  1. T1
  2. YACT<T2, T3>

The second one is something complex, and is expressed through YACT itself and a couple of template parameters of the specializations, but YACT<T2, T2> remain a single template argument, from the YACT point of view.

Substantially, declaring

template<typename T1, typename T2>
class YATC;

you declare that YACT receive exactly two types; nothing forbid you to construct your types in a very very complex way

//                1                                  2
//...vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv..vvvvvvvvvvvvvvvvvvvvvvvvv
YACT<std::tuple<YACT<int, long>>, char>, std::vector<std::string>>> foo;

and declare YACT specializations that use a lot of template parameter (not only types but also values and template-template), to express the couple the YACT types parameter. But YACT has to receive exaclty two types parameters. Complex as you will but exactly two.

I still cannot insatiate the class with YATC<int, int, double>, only YATC<int, YATC<int, double>>;

Exactly.

Because with

//    1    2      3
// ..VVV..VVV..VVVVVV
YATC<int, int, double>

you pass three types to YACT, but with

//    1           2
// ..VVV..VVVVVVVVVVVVVVVVV
YATC<int, YATC<int, double>>

you pass two types to YACT (YACT<int, double> is a single type).