[英]“typename” and “template” keywords: are they really necessary?
[英]Again on typename and template keywords
我已经仔细阅读了很多关于这个主题的答案,但是在非模板函数(嵌套模板类的成员)的范围内,当这两个关键字不需要这些关键字时,我无法弄清楚。
我的参考编译器是GNU g ++ 4.9.2和clang 3.5.0。
它们在下面的代码中几乎没有什么不同,我把嵌入的注释试图解释会发生什么。
#include <iostream>
// a simple template class with a public member template struct
template <class Z>
class Pa
{
// anything
public:
template <class U>
struct Pe // a nested template
{
// anything
void f(const char *); // a non-template member function
};
template <class U> friend struct Pe;
};
// definition of the function f
template <class AAA>
template <class BBB>
void Pa<AAA> :: Pe<BBB> :: f(const char* c)
{
Pa<AAA> p; // NO typename for both clang and GNU...
// the following line is ACCEPTED by both clang and GNU
// without both template and typename keywords
// However removing comments from typename only
// makes clang still accepting the code while GNU doesn't
// accept it anymore. The same happens if the comments of template
// ONLY are removed.
//
// Finally both compilers accept the line when both typename AND
// template are present...
/*typename*/ Pa<AAA>::/*template*/ Pe<BBB> q;
// in the following clang ACCEPTS typename, GNU doesn't:
/*typename*/ Pa<AAA>::Pe<int> qq;
// the following are accepted by both compilers
// no matter whether both typename AND template
// keywords are present OR commented out:
typename Pa<int>::template Pe<double> qqq;
typename Pa<double>::template Pe<BBB> qqqq;
std::cout << c << std::endl; // just to do something...
}
int main()
{
Pa<char>::Pe<int> pp;
pp.f("bye");
}
那么,在f
的范围内是否是Pa<double>::Pe<BBB>
一个从属名称?
那么Pa<AAA>::Pe<int>
怎么样?
毕竟,为什么两个引用编译器的这种不同行为?
任何人都可以澄清解决难题吗?
[temp.res]中的重要规则是:
当qualified-id旨在引用不是当前实例化成员的类型(14.6.2.1)并且其嵌套名称说明符引用依赖类型时,它应以关键字
typename
作为前缀,形成typename-specifier 。 如果typename-specifier中的qualified-id不表示类型,则程序格式错误。
这个问题围绕着两个合格身份 :
Pa<double>::Pe<BBB>
Pa<AAA>::Pe<int>
首先,什么是依赖类型? 根据[temp.dep.type]:
如果是,则类型依赖于
- 模板参数,
- 未知专业的成员,
- 作为当前实例化的从属成员的嵌套类或枚举,
- cv-qualified类型所依赖的cv限定类型,
- 由任何依赖类型构成的复合类型,
- 一个数组类型,其元素类型是依赖的或其边界(如果有)是依赖于值的,
- 一个simple-template-id ,其中模板名称是模板参数,或者任何模板参数是依赖类型或依赖于类型或依赖于值的表达式,或者
- 由decltype
( 表达式 )表示,其中表达式依赖于类型(14.6.2.2)。
Pa<double>
(第一个示例的嵌套名称说明符 )不是依赖类型,因为它不适合任何项目符号。 由于我们不符合该标准,因此我们不需要为typename
关键字添加前缀。
但是, Pa<AAA>
是一个依赖类型,因为它是一个simple-template-id ,其中一个模板参数是一个依赖类型( AAA
通常是一个依赖类型,因为它是一个模板参数)。
那么,什么是“当前实例化的成员”?
名称是指当前实例化(如果是)
- [...]
- 在主类模板的定义或主类模板的成员中,类模板的名称后跟<>中包含的主模板的模板参数列表(如下所述)(或等效的模板别名专用) )“ - 在类模板的嵌套类的定义中,作为当前实例化的成员引用的嵌套类的名称,或者
在这种情况下,当前实例化是Pa<AAA>
(或者也是Pa
)。 和:
名称是当前实例化的成员,如果它是[...] qualified-id ,其中嵌套名称说明符引用当前实例化,并且当查找时,引用类的至少一个成员这是当前实例化或其非依赖基类。
所以Pe
是当前实例化的成员。 因此,虽然Pa<AAA>::Pe<int>
的嵌套名称说明符是依赖类型,但它是一个类型,它是当前实例化的成员,因此您不需要关键字typename
。 请注意, Pa<AAA>::Pe<int>
本身是一个依赖类型(它是一个嵌套类,它是当前实例化的依赖成员),但这本身就意味着需要typename
关键字。
gcc在这里不接受typename这一事实:
/*typename*/ Pa<AAA>::Pe<int> qq;
因为它想要
typename Pa<AAA>::template Pe<int> qq;
是个bug。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.