Again on typename and template keywords
我已经仔细阅读了许多关于这个主题的答案,但是我仍然不能确切地知道这两个关键字在作为嵌套模板类成员的非模板函数的作用域中是什么时候需要或不需要。
我的参考编译器是GNU G++4.9.2和Clang3.5.0。
在我把Embedded放在下面的代码中,它们的行为几乎没有什么不同试图解释发生了什么的评论。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #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"); } |
那么,在
那么
而且,毕竟,为什么这两个引用的编译器的行为不同?
有人能澄清解决这个难题吗?
[温度响应]中的重要规则是:
When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1)
and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keywordtypename , forming
a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.
此问题将在两个限定ID之间撤销:
1 2 | Pa<double>::Pe<BBB> Pa<AAA>::Pe<int> |
首先,什么是从属类型?根据[温度深度类型]:
A type is dependent if it is
— a template parameter,
— a member of an unknown specialization,
— a nested class or enumeration that is a dependent member of the current instantiation,
— a cv-qualified type where the cv-unqualified type is dependent,
— a compound type constructed from any dependent type,
— an array type whose element type is dependent or whose bound (if any) is value-dependent,
— a simple-template-id in which either the template name is a template parameter or any of the template
arguments is a dependent type or an expression that is type-dependent or value-dependent, or
— denoted bydecltype (expression), where expression is type-dependent (14.6.2.2).
但是,
那么,"当前实例化的成员"是什么?
A name refers to the current instantiation if it is
— [...]
— in the definition of a primary class template or a member of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <> (or an equivalent template alias specialization)"
— in the definition of a nested class of a class template, the name of the nested class referenced as a
member of the current instantiation, or
在这种情况下,当前的实例化是
A name is a member of the current instantiation if it is [...] A qualified-id in which the nested-name-specifier refers to the current instantiation and that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.
所以
GCC不接受这里的类型名:
1 | /*typename*/ Pa<AAA>::Pe<int> qq; |
因为它想要
1 | typename Pa<AAA>::template Pe<int> qq; |
是一个bug。