无法理解关于非专用类模板的 menber 显式专用化声明的规则

Can't understand the rule about explicit specialization declaration for menber of an unspecialized class template

本文关键字:专用 menber 规则 声明 于非      更新时间:2024-05-09

最新 C++ 标准中非专用类模板成员的显式专用化声明规则如下:

在类模板的成员

或出现在命名空间作用域中的成员模板的显式专用化声明中,成员模板及其某些封闭类模板可能保持非专用化状态,但如果类成员模板的封闭类模板也未显式专用化,则声明不得显式专用化类成员模板。在这种明确的专用化声明中,应在成员的显式专用声明之前提供关键字模板后跟模板参数列表<>而不是模板。模板参数列表中的模板参数类型应与主模板定义中指定的类型相同。

老实说,我对这一段有很多困惑。请考虑此规则的以下示例中编写的示例。

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

正如评论所说,这种针对会员mf2的明确专业化声明格式不正确,但是,我不明白为什么通过这个规则来形成这种表达方式不正确。 我的理由是粗体字,它说如果包含类模板的封闭类模板也没有明确专用,则声明不应明确专用于类成员模板。 但是,在此示例中,声明是mf2的显式专用化,它不是类成员模板,而是类模板的成员。所以严格来说,它不符合例外的条件,为什么声明格式不正确?我觉得这一段不清楚。因此,进一步挖掘,我发现了有缺陷的报告,即CWG529。

它说:

在类模板

的成员或出现在命名空间范围的成员模板的显式专用化声明中,成员模板及其某些封闭类模板可能保持非专用化,也就是说,相应的模板前缀可以指定模板参数列表而不是模板<>并且使用这些模板参数作为模板参数编写命名模板的模板 id。在此类声明中,模板参数的数量、种类和类型应与主模板定义中指定的相同,并且模板参数在模板 id 中的命名顺序应与它们在模板参数列表中出现的顺序相同。在命名成员的限定 ID 中,非专用模板 ID 不得位于模板专用化名称之前

多想了一会儿,我还是觉得这个提案不足以解释这些情况,比如:

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(){}

首先,mf2不是模板专用化,但是模板 idB<T>mf2之前,此声明的格式不正确。它仍然无法解释此示例:

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是模板名称,但不是模板 ID(即模板专用化)

那么在思考了这些格式错误的例子之后,恕我直言,这个修改后的句子是这个规则的意图吗?

在类模板

的成员或出现在命名空间作用域的成员模板的显式专用化声明中,成员模板及其某些封闭类模板可能保持非专用化状态。在这种明确的专用化声明中,应在成员的显式专用声明之前提供关键字模板后跟模板参数列表<>而不是模板。模板参数列表中的模板参数类型应与主模板定义中指定的类型相同。在此声明中,嵌套名称说明符中非专用名称说明符中的每个template-id都应显式专用化

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){}

此示例的格式不正确,尽管B<T>不是专用的,但它已被显式专用化。这是一个很好的解释吗?或者,如果我误读了原始规则,请解释如何理解它。

规则 [temp.expl.spec]/p16 以递归方式应用,它简单地说,除非导致它的整个链也是专门的,否则没有什么是专门的。

我们甚至没有mf2,因为不可能针对每种可能A进行专门的B

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(){}

同样的故事:不能针对每一种可能A<int>::B专门mf2

最后一个具有专用A<int>::B的示例有些令人困惑,但相同的规则仍然适用:

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试图解决这样一个事实,即[temp.expl.spec]/p16中的措辞令人困惑,因为它谈论的是嵌套类的"专业化">,而实际上并没有声明这种专业化。恕我直言,CWG 529 未能承认每个嵌套级别都有专业化,只是它是假设的。给出第一个示例:

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() { }

没有template <class Y> template <> class A<Y>::B {};.但是如果有的话,它就不会编译(同样,不能为每个可能的A专门化B)。