Vlad from Moscow
Vlad from Moscow

Reputation: 310910

Class template partial specialization: compiler error

This program

#include <iostream>

template <int I>
struct A
{
    A() { std::cout << "A<I>()\n"; }
};

template <int I>
struct A<I + 5>
{
    A() { std::cout << "A<I + 5>()\n"; }
};


int main()
{
    return 0;
}

is compiled neither by gcc HEAD 10.0.0 20190 nor by clang HEAD 10.0.0.

For example the gcc compiler issues error

prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
   10 | struct A<I + 5>
      |        ^~~~~~~~

Is there a wrong class template partial specialization?

Upvotes: 1

Views: 812

Answers (2)

Max Langhof
Max Langhof

Reputation: 23681

Looking into temp.class.spec.match/3, we have

If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.

with the example

template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {};     // error

template <int I> struct A<I, I> {};         // OK

template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {};    // OK

Clang's error message echoes this:

error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used

(See @StoryTeller's answer for why this deduction fails in your code, I won't duplicate that here.)

Upvotes: 2

This is not a valid partial specialization (though the error could be better). The clause we are not inline with is the following:

[temp.class.spec]

8 Within the argument list of a class template partial specialization, the following restrictions apply:

  • The specialization shall be more specialized than the primary template.

"The specialization is not more specialized!?" I suppose you think. But that is indeed the case. The rules for determining which is "more specialized" are described in [temp.class.order]. The gist of it is that we are to consider two hypothetical function template overloads:

template <int I>
struct A { /* ... */ };

template<int I>
void foo(A<I>); //1

template<int I>
void foo(A<I + 5>); //2

We then perform overload resolution and partial ordering of the function templates. If #2 wins, it's more specialized and your declaration is legal. If it doesn't win, the declaration is invalid. Partial ordering is done by cooking up some arguments and doing template argument deduction of one template against another. So suppose we start with comparing the first to the second (I'll rename them for simplicity, but they are still overloads):

void foo1(A<0>); -> void foo2(A<I + 5>);

Does the argument deduction succeed here? It doesn't. I + 5 is a non-deduced context:

[temp.deduct.type]

The non-deduced contexts are:

5.3 - A non-type template argument or an array bound in which a subexpression references a template parameter.

The I references a template parameter, so I + 5 is a non-deduced context. Therefore template argument deduction fails in this direction.

Let's try the other direction. Again we cook up an argument

void foo2(A<1 + 5>); = void foo2(A<6>);  -> void foo1(A<I>); 

Deduction obviously succeeds here. So according to the rules of function template partial ordering, foo1 is more specialized than foo2. This means that our primary is really more specialized than our "partial specialization", making the partial specialization ill-formed.

Upvotes: 7

Related Questions