【问题标题】:Again on typename and template keywords再次关于 typename 和 template 关键字
【发布时间】:2015-09-03 13:36:38
【问题描述】:

我已经仔细阅读了许多关于这个主题的答案,但是我无法确切地知道这两个关键字何时在嵌套模板类的成员的非模板函数的范围内是或不需要的。

我的参考编译器是 GNU g++ 4.9.2 和 clang 3.5.0。

在我嵌入的以下代码中,它们的行为几乎没有什么不同 cmets 试图解释发生了什么。

#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&lt;double&gt;::Pe&lt;BBB&gt; 是否是从属名称?

那么Pa&lt;AAA&gt;::Pe&lt;int&gt; 呢?

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

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

【问题讨论】:

  • “它们的行为几乎没有什么不同”......据我了解你的问题,你想知道为什么它们的行为不同,在这种情况下,“几乎”是错误的词。实际上,您所描述的是它们的行为几乎不一样;)
  • GCC 似乎无法再处理这个名字了,只要你撒上typenametemplate 并且“忘记”P&lt;AAA&gt; 是环绕的模板。我认为 GCC 在这里有一个错误,并建议进行 PR。这里不需要typenametemplate
  • 最近放宽了严格不需要的允许 typename 的规则。可能在 gcc 4.9 发布之后。可以解释示例 3。
  • Pa&lt;double&gt;::Pe&lt;BBB&gt; 是一个从属名称,因为它的类型取决于BBB。但这并不意味着您需要插入typenamePa&lt;AAA&gt;::Pe&lt;int&gt;Pa&lt;AAA&gt; 也是如此。在所有这些情况下,编译器都可以确定Pe 是一个模板,而Pe&lt;int&gt; / Pe&lt;BBB&gt; 是一个类型。见stackoverflow.com/a/17579889/34509

标签: c++ templates typename dependent-name


【解决方案1】:

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

qualified-id 旨在引用不是当前实例化成员的类型时 (14.6.2.1) 并且它的nested-name-specifier是一个依赖类型,它应该以关键字typename为前缀,形成 类型名称说明符。如果 typename-specifier 中的 qualified-id 不表示类型,则程序是非良构的。

这个问题围绕着两个qualified-id

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

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

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

Pa&lt;double&gt;(第一个示例的 nested-name-specifier)不是依赖类型,因为它不符合任何要点。由于我们不符合该标准,因此我们不需要为 typename 关键字添加前缀。

Pa&lt;AAA&gt;,然而,一个依赖类型,因为它是一个 simple-template-id,其中一个模板参数是一个依赖类型 (@987654327 @ 是一个微不足道的依赖类型,因为它是一个模板参数)。

那么,什么是“当前实例化的成员”?

名称指的是当前实例化,如果它是
— [...]
— 在主类模板或主类模板成员的定义中,类模板的名称后跟主模板的模板参数列表(如下所述),括在 (或等效的模板别名特化)中)" — 在类模板的嵌套类的定义中,嵌套类的名称被引用为 当前实例化的成员,或

在这种情况下,当前实例化为Pa&lt;AAA&gt;(或者,也为Pa)。并且:

如果一个名称是 [...] 一个 qualified-id 其中 nested-name-specifier 指的是当前实例,则它是当前实例化的成员实例化,并且在查找时,它指的是作为当前实例化的类的至少一个成员或其非依赖基类。

所以Pe 是当前实例化的成员。因此,虽然Pa&lt;AAA&gt;::Pe&lt;int&gt;nested-name-specifier 是依赖类型,但它是当前实例化的成员类型,因此您不需要关键字typename。请注意,Pa&lt;AAA&gt;::Pe&lt;int&gt; 本身就是一个依赖类型(它是一个嵌套类,是当前实例化的依赖成员),但这本身确实意味着需要 typename 关键字。

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

/*typename*/ Pa<AAA>::Pe<int> qq;

因为它想要

typename Pa<AAA>::template Pe<int> qq;

是一个错误。

【讨论】:

    猜你喜欢
    • 2014-03-30
    • 1970-01-01
    • 2018-12-03
    • 2021-05-15
    • 2011-05-24
    • 2011-01-02
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多