1
votes

The rule of explicit specialization declaration for member of an unspecialized class template in the latest c++ standard is as the following:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. In such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition.

Honestly, I have many confusions about this paragraph. Please consider the example written in the following of this rule.

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};
template <class Y> 
template <>
void A<Y>::B<double>::mf2() { }   // error: B<double> is specialized but  
                                  // its enclosing class template A is not

As the comment says, this explicit specialization declaration for member mf2 is ill-formed, however, I can't understand why this delcaration is ill-formed through this rule. My reason is the bolded wording, it says that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well. However, in this example, the declaration is a explicit specialization for mf2 which is not a class member template instead it is a member of a class template. So strictly speaking, it does not conform the condition of the exception, why the declaration is ill-formed? I feel this paragraph is unclear. So digging in the further, I found the defective report that is CWG529.

It says that:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, that is, the corresponding template prefix may specify a template-parameter-list instead of template<> and the template-id naming the template be written using those template-parameters as template-arguments. In such a declaration, the number, kinds, and types of the template-parameters shall be the same as those specified in the primary template definition, and the template-parameters shall be named in the template-id in the same order that they appear in the template-parameter-list. An unspecialized template-id shall not precede the name of a template specialization in the qualified-id naming the member.

After thinking for a bit more, I still think the proposal is not sufficient to interpret these cases, for example:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template<>
template <class T> 
void A<int>::B<T>::mf2(){}

First, mf2 is not a template specialization, however the template-id B<T> precedes mf2, this declaration is ill-formed. It still can't interpret this example too:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template<>
template <class T> 
template <class U> 
void A<int>::B<T>::mf1(U){}

mf1 is a template name but not a template-id (i.e, template specialization)

So after thinking these ill-formed examples, IMHO, Is this modified sentence the intent of this rule?

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized. In such an explicit specialization declaration, the keyword template followed by a template-parameter-list shall be provided instead of the template<> preceding the explicit specialization declaration of the member. The types of the template-parameters in the template-parameter-list shall be the same as those specified in the primary template definition. In this declaration, each template-id in nested-name-specifier that is unspecialized shall be explicit specialized.

template <class T1> class A {
  template<class T2> class B {
    template<class T3> 
    void mf1(T3);
    void mf2();
  };
};

template<>   // explicit specialization for `B`
template<class T>
class A<int>::B{
   template<class U>
   void mf1(U);
};

template<>
template<class T>
template <class U> 
void A<int>::B<T>::mf1(U){}

This example will be ill-formed, although B<T> is unspecialized but it has been explicit specialized. Is this a good interpretation? Or, If I misread the original rule, please interpret how to understand it.

1

1 Answers

1
votes

The rule [temp.expl.spec]/p16 applies recursively, it says simply that nothing can be specialized unless the entire chain leading up to it is also specialized.

We don't even get to mf2 because it's not possible to specialize B for every possible A:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template <class Y> 
template <>
void A<Y>::B<double>::mf2() { }  // Not OK - can't specialize B for every possible A

template <> 
template <>
void A<int>::B<double>::mf2() { }  // OK - full specialization
template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template <>
template <class T> 
void A<int>::B<T>::mf2(){}

Same story: can't specialize mf2 for every possible A<int>::B.

The last example with a specialized A<int>::B is somewhat confusing, but the same rule still applies:

template <class T1> class A {
template <class T2> class B {
    template<class T3> 
    void mf1(T3);
    void mf2();
};
};

template <>
template <class T>
class A<int>::B {   // explicit specialization for `B`
    template<class U>
    void mf1(U);
};

template <>
template <class T>
template <class U> 
void A<int>::B<T>::mf1(U){} // OK (not a specialization, but an out-of-line definition)

template <>
template <class T>
template <> 
void A<int>::B<T>::mf1(double){} // Not OK again, the entire chain must be specialized

template <>
template <>
template <> 
void A<int>::B<double>::mf1(double){} // OK - full specialization

CWG 529 attempts to address the fact that the wording in [temp.expl.spec]/p16 is confusing in that it talks about a "specialization" of a nested class when no such specialization was actually declared. IMHO, CWG 529 fails to acknowledge that there is a specialization at every nesting level, only it is hypothetical. Given the first example:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template <class Y> 
template <>
void A<Y>::B<double>::mf2() { }

There is no template <class Y> template <> class A<Y>::B {};. But if there was one, it would not have compiled (again, can't specialize B for every possible A).