Let's say I have this code
template<typename T2, typename T = int>
struct X
{
static double f;
};
template<typename T>
double X<T>::f = 14.0;
If I try to compile clang give me the following error
nested name specifier 'X::' for declaration does not refer into a
class, class template or class template partial specialization
and for GCC :
error: template definition of non-template 'double X::f'
The question is :
Why the compiler want us to specialize the struct X like that :
template<typename T2>
struct X<T2,int>
{
static double f;
};
The first declaration has int
as a default argument, why the compiler don't choose this declaration ?
I searched in the standard anchor [temp.spec] but it didn't help.
I ask this question after answered this one on SO.
Thanks for your help !
"Why the compiler want us to specialize the struct X like that" - that's not what the error messages are saying. You don't need to do this, and you really shouldn't do it unless what you want is a partial specialization and a static member defined only for that partial specialization.
The problem is that template<typename T2, typename T = int> struct X
is a class template that has two template parameters. The fact that the second one has a default template argument doesn't change the fact that there are still two parameters.
So, you need to define your class template member as belonging to a class template with two parameters, like this:
template<typename T2, typename T>
double X<T2, T>::f = 14.0;
The relevant paragraphs in the standard (N4527, the current draft):
[14.5.1p3]
When a member function, a member class, a member enumeration, a static
data member or a member template of a class template is defined
outside of the class template definition, the member definition is
defined as a template definition in which the template-parameters are
those of the class template. The names of the template parameters used
in the definition of the member may be different from the template
parameter names used in the class template definition. The template
argument list following the class template name in the member
definition shall name the parameters in the same order as the one used
in the template parameter list of the member. Each template parameter
pack shall be expanded with an ellipsis in the template argument list.
[14.1p9]
[...] A default template-argument shall not be specified in the
template-parameter-lists of the definition of a member of a class
template that appears outside of the member’s class. [...]
As specified in the quote above, the actual names of the template parameters (T2
and T
) don't matter, they can be different from the ones in the class template definition, but they need to be consistent within the definition of the member. That is, you can do this
template<typename T, typename U>
double X<T, U>::f = 14.0;
and it will still define the member of the correct X
class template. However, using the same names can make things easier to understand when reading the code.
By defining the partial specialization before the definition of f
in your original example, template<typename T> double X<T>::f = 14.0;
becomes a valid definition of the member f
of the partial specialization template<typename T2> struct X<T2,int>
, and only of that template (partial specializations are templates themselves). The member f
of the primary template template<typename, typename> struct X
remains undefined.
The relevant wording is in [14.5.5.3p1]:
The template parameter list of a member of a class template partial
specialization shall match the template parameter list of the class
template partial specialization. The template argument list of a
member of a class template partial specialization shall match the
template argument list of the class template partial specialization. A
class template specialization is a distinct template. The members of
the class template partial specialization are unrelated to the members
of the primary template. [...]