I am trying to do a simple partial template specialization, but I get errors on g++4.4.7, g++4.8.5, clang++3.8.0. Whenever I mention compiler(s) error, I mean the output of all of these, as they always agree. I am using C++03, compiling without any option.
The code:
#include <iostream>
template <typename T, typename X, typename G>
struct A {};
template <typename T, typename X>
struct A<T, X, void> { A() : n(1) {} X n; T b; };
template <typename X>
struct A<X, void, void> { A() : n(2) {} X n; };
int main() {
A<int, float> one;
A<int> two;
std::cout << one.n << " | " << two.n << "\n";
return 0;
}
Question 1: This code fails to compile. The compilers say that A<int, float>
and A<int>
are wrong as A
requires 3 templates parameters. Why?
If I change the original declaration to
template <typename T, typename X = void, typename G = void>
struct A {};
The code compiles and the output is: 1 | 2
.
What happens is that the compiler in a first step matches one
and two
type to the not specialized A
, but then it correctly decides to use the code of the partially specialized class one would expect it to use. But it should not need the defaults.
I then decide to change the last partial specialization switching the first and second parameter:
template <typename X>
struct A<void, X, void> { A() : n(2) {} X n; };
I would expect this to change nothing, but the compilers disagree. The clearest output between the 3 is here reported:
a.cpp:7:40: error: field has incomplete type 'void'
struct A<T, X, void> { A() : n(1) {} X n; T b; };
^
a.cpp:14:10: note: in instantiation of template class 'A<int, void, void>' requested here
A<int> two;
^
1 error generated.
Question 2: Why are the compilers considering the two
variable an instance of the partial specialization of A that specializes only one argument?
Note that is the "2nd matching", because if I only use 1 default template argument, the compiler will go back to complaining about the fact that 3 template parameters are needed.
Thanks.
Question 1: This code fails to compile. The compilers say that
A<int, float>
andA<int>
are wrong asA
requires 3 templates parameters. Why?
Because A
requires 3 template parameters. You declared A
as:
template <typename T, typename X, typename G>
struct A {};
There is no two- or one-template parameter version of A
. There are versions specialized on some of the types being void
, but that's still a parameter - not an absence of parameter.
When you add the defaults, then A<int, float>
evaluates as A<int, float, void>
, which is a valid instantiation - and picks the specialization which sets n
to 1.
You're misunderstanding how specialization works. Specialization doesn't change the number of template parameters. It's just a way of adding special functionality depending on what the template parameters end up being.
Question 2: Why are the compilers considering the two variable an instance of the partial specialization of
A
that specializes only one argument?
We have three choices
template <T, X, G> struct A; // the primary
template <T, X, void> struct A; // (1)
template <void, X, void> struct A; // (2)
When we instantiate A<int>
, that is the same as A<int, void, void>
when we add in the default parameters. That does not match (2)
- because that one requires the first parameter to be void
and yours is int
. (1)
is a better match than the primary since it's more specialized. But then, (1)
has a member of type X
and in this case X
is deduced as void
(from the default parameter), and that's not allowed.