Template struct with the default template argument

2019-05-03 15:24发布

问题:

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 !

回答1:

"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. [...]