同样关于typename和template关键字

时间:2015-09-03 13:36:38

标签: c++ templates typename dependent-name

我仔细阅读了很多关于这个主题的答案,但是在非模板函数(嵌套模板类的成员)的范围内需要这两个关键字时,我还是无法理解这些关键字。

我的参考编译器是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>呢?

毕竟,为什么两个引用编译器的这种不同行为?

任何人都可以澄清解决难题吗?

1 个答案:

答案 0 :(得分:3)

[temp.res]中的重要规则是:

  

qualified-id 旨在引用不是当前实例化成员的类型时(14.6.2.1)   并且它的嵌套名称说明符是指依赖类型,它应以关键字typename为前缀,形成    typename-specifier 。如果 typename-specifier 中的 qualified-id 不表示类型,则程序格式错误。

问题围绕两个 qualified-id s:

进行
Pa<double>::Pe<BBB>
Pa<AAA>::Pe<int>

首先,什么是依赖类型?根据[temp.dep.type]:

  

如果是类型,则类型取决于    - 模板参数,
   - 未知专业的成员,
   - 作为当前实例化的从属成员的嵌套类或枚举,
   - cv-qualified类型所依赖的cv限定类型,
   - 由任何依赖类型构成的复合类型,
   - 一个数组类型,其元素类型是依赖的或其边界(如果有的话)是依赖于值的,
   - simple-template-id ,其中模板名称是模板参数或任何模板   arguments是依赖类型或依赖于类型或依赖于值的表达式,或者是    - 由decltype表达式)表示,其中表达式与类型有关(14.6.2.2)。

Pa<double>(第一个示例的 nested-name-specifier )不是依赖类型,因为它不适合任何项目符号。由于我们不符合该条件,因此我们不需要在typename关键字前加上前缀。

Pa<AAA>但是, 是一个依赖类型,因为它是 simple-template-id ,其中一个模板参数是一个依赖类型( AAA通常是依赖类型,因为它是模板参数。)

然后,&#34;当前实例化的成员&#34;?

  

名称是指当前实例化(如果是)    - [...]
   - 在主类模板的定义或主类模板的成员中,类模板的名称后跟包含在&lt;&gt;中的主模板的模板参数列表(如下所述)。 (或等效模板别名专业化)&#34;    - 在类模板的嵌套类的定义中,引用为的嵌套类的名称   当前实例化的成员,或

在这种情况下,当前实例化是Pa<AAA>(或者,Pa)。和

  

名称是当前实例化的成员,如果它是一个 qualified-id ,其中嵌套名称说明符指的是当前实例化,当查找时,指的是一个类的至少一个成员,它是当前实例化或其非依赖基类。

所以Pe是当前实例化的成员。因此,虽然Pa<AAA>::Pe<int>嵌套名称说明符是依赖类型,但它是当前实例化的成员类型,因此您不需要关键字{{1 }}。请注意typename 是一个依赖类型本身(它是一个嵌套类,它是当前实例化的依赖成员),但这本身就意味着{{1} } keyword是必需的。

gcc在这里不接受typename的事实:

Pa<AAA>::Pe<int>

因为它想要

typename

是一个错误。