再次强调typename和template关键字
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]中的重要规则是:
当限定id打算引用不是当前实例化成员的类型时(14.6.2.1)且其嵌套名称说明符表示依赖类型,则应在其前面加上关键字
typename
,形成 typename-specifier 。如果类型说明符中的限定id不表示类型,则程序是病态的。
问题围绕两个限定ids:
Pa<double>::Pe<BBB>
Pa<AAA>::Pe<int>
首先,什么是依赖类型?根据[temp. deep .type]:
如果是
,则类型是依赖的-模板参数,
-未知专门化的成员,
-当前实例化的依赖成员的嵌套类或枚举,
- cv-限定类型,其中cv-限定类型是依赖的,
-由任何依赖类型构造的复合类型,
-元素类型依赖或边界(如果有的话)依赖于值的数组类型,
- a simple-template-id,模板名可以是模板参数,也可以是模板中的任意一个参数是依赖类型或依赖于类型或值的表达式,或者
-用decltype
表示(表达式),其中表达式是类型依赖的(14.6.2.2)。
Pa<double>
(第一个示例的嵌套名称说明符)不是依赖类型,因为它不适合任何项目符号。由于我们不符合该标准,因此不需要在typename
关键字前加上前缀。
Pa<AAA>
是依赖类型,因为它是simple-template-id,其中一个模板实参是依赖类型(AAA
通常是依赖类型,因为它是模板形参)。
那么什么是"当前实例化的成员"?
如果
是,则名称是指当前实例化- - - - - -[…]
-在主要类模板或主要类模板成员的定义中,类模板的名称后跟主要模板的模板参数列表(如下所述),其中包含<>(或等效的模板别名专门化)在类模板的嵌套类的定义中,引用的嵌套类的名称为成员,或
在本例中,当前实例是Pa<AAA>
(或者Pa
)。和:
如果名称是[…],则它是当前实例化的成员。限定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;
是一个错误。
- Visual Studio 2015:Extern "C" 和 "export" 关键字
- 表示"accepting anything for this template argument" C++概念的通配符
- C++中的"inline"关键字
- 如何确保C++函数在定义之前声明(如override关键字)
- 传递给std::function template的template参数究竟代表什么
- 在template中使用std::variant的template函数
- 谷歌模拟和覆盖关键字
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- 如果全局变量默认是外部变量,为什么要添加"extern"关键字?
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- 为什么"delete"关键字不删除节点?
- 在 c++ 中正确定义"this"关键字?
- 这个额外的关键字在这个 c++ 类声明中是什么意思?
- C++ template for QList
- 在 typedef 内部使用 const 关键字和在 typedef 外部使用 const 关键字之间有区别吗?
- C++ - 为什么这里需要'template'关键字?
- "template"关键字限定符是否会导致代码成功编译,但不同?
- Clang(OS X)要求在特定嵌套声明中"template"关键字,而VS禁止它
- 再次强调typename和template关键字
- 函数声明中template关键字的含义