10 Aug 2009 (updated 10 Aug 2009 at 17:54 UTC)
»
error: template parameters not used in partial
specialization:
Dear search engines,
please direct people to this explanation if they search for
the GCC error template parameters not used in partial
specialization.
An undocumented change to GCC 4.3
means that g++ now rejects some invalid template partial
specializations that used to be accepted.
Examples of code that used to be accepted can be found in
bugs 35989
and 41028.
GCC was wrong to accept them, and is now fixed.
Here is an example that compiled with GCC 4.2 but is
rejected by GCC 4.3 and later:
#include <vector>
#include <cassert>
template<typename T>
struct A
{
static const bool value = false;
};
template<typename U>
struct A<typename std::vector<U>::iterator>
{
static const bool = true;
};
int main()
{
assert( !A<int>::value );
assert( A<std::vector<int>::iterator>::value );
}
The meta-function A
attempts to detect when its
template argument T
is the
iterator
type belonging to
std::vector<U>
for some type
U
, by providing a partial specialization to
match vector iterator types.
GCC 4.2 and earlier will compile this code, but the second
assertion will fail:
a.out: t.cc:19: int main(): Assertion
`A<std::vector<int>::iterator>::value' failed.
Aborted (core dumped)
The partial specialization is not used!
GCC 4.3 will determine that the partial specialization will
never be used, so will not compile it:
t.cc:11: error: template parameters not used in partial
specialization:
t.cc:11: error: 'U'
Looking at the partial specialization it seems that
U
is used, so let's look at why the code
does not work. The C++ standard says, in
[temp.class.spec.match] paragraph 2:
A partial specialization matches a given actual template
argument list if the template arguments of the partial
specialization can be deduced from the actual template
argument list
So in order for an instantiation,
A<X>
,
to match the partial specialization, the compiler must be
able to deduce the type
U
from
X
.
In the example above,
X
is
std::vector<int>::iterator
, so why can
the compiler not deduce
U
as
int
?
The rules for deducing template arguments from types in
[temp.deduct.type] say that template arguments can only be
deduced in certain contexts, and that in other contexts the
actual template arguments do not participate in type
deduction. U
appears in the
nested-name-specifier of the qualified-id
std::vector<U>::iterator
, and this is
defined as a nondeduced context by paragraph 4.
The compiler cannot deduce U
when instantiating
the template, so the partial specialization is ill-formed.
The more persistent and curious might wonder why that is a
nondeduced context. Well, consider that to match
X
with some
std::vector<U>::iterator
the compiler would
have to instantiate vector
with an arbitrary
number of types (maybe every type in the program!) to see if
there is a nested type iterator
that matches
X
. Performing all those instantiations could
involve an arbitrary number of partial and explicit
specializations of vector
and not only would
many of those instantiations fail (resulting in errors that
must be suppressed via SFINAE) but the compilation would
require huge amounts of time and memory. Parsing C++
templates is complicated enough already, so the compiler
doesn't even try to perform this particular miracle.