为什么是关键字" typename"在合格的附属名称之前需要,而不是在合格的独立名称之前

时间:2011-12-21 02:28:57

标签: c++ templates typename

class A
{
   static int iterator;
   class iterator
   {
      [...]
   };
   [...]
};

我(我想)理解这里需要typename的原因:

template <class T>
void foo() {
   typename T::iterator* iter;
   [...]
}

但我不明白为什么不需要typename

void foo() {
   A::iterator* iter;
   [...]
}

任何人都可以解释一下吗?


编辑:

编译器没有后者问题的原因,我发现在评论中得到了很好的回答:

A::iterator的情况下,

我不明白为什么编译器不会将它与static int iterator混淆? - xcrypt

@xcrypt因为它知道A::iterator是什么,可以根据它的使用方式选择哪一个 - Seth Carnegie


编译器在合格的从属名称之前需要typename的原因在我看来在Kerrek SB接受的答案中得到了很好的回答。一定要阅读有关该答案的评论,尤其是iammilind的评论:

“T :: A * x ;,对于T :: A是一个类型且T :: A是一个值的情况,这个表达式都可以为真。如果A是一个类型,那么它将导致指针声明;如果A是一个值,那么它将导致乘法。因此,单个模板对于两种不同的类型将具有不同的含义,这是不可接受的。“

3 个答案:

答案 0 :(得分:41)

C ++中的名称可以涉及三个不同的实体层:值,类型和模板。

struct Foo
{
    typedef int A;
    static double B;
    template <typename T> struct C;
};

三个名称Foo::AFoo::BFoo::C是所有三个不同层级的示例。

在上面的示例中,Foo是一个完整的类型,因此编译器已经知道Foo::A等引用的内容。但现在想象一下:

template <typename T> struct Bar
{
    T::A x;
};

现在我们遇到了麻烦:T::A是什么?如果T = Foo,那么T::A = int,这是一种类型,一切都很好。但是当T = struct { char A; };时,T::A,这是没有意义的。

因此,编译器要求告诉它T::AT::B以及T::C 假设是什么。如果你什么都不说,那就假定它是一个值。如果你说typename,它是一个类型名称,如果你说template,它就是一个模板:

template <typename T> struct Bar
{
    typename T::A x;    // ah, good, decreed typename

    void foo()
    {
        int a = T::B;   // assumed value, OK

        T::template C<int> z;  // decreed template
        z.gobble(a * x);
    }
};

辅助检查,例如T::B是否可转换为intax是否可以相乘,以及C<int>是否确实具有成员函数gobble都被推迟,直到您实际实例化模板。但是,名称表示值,类型或模板的规范是代码语法正确性的基础,必须在模板定义期间直接提供。

答案 1 :(得分:2)

因为在非模板foo中,您正在进行有效的乘法运算(假设您在使用之前声明了iter)。尝试省略星号,您将收到编译器错误。 int隐藏了这个类。

typename关键字阻止隐藏。只有gcc不正确地执行它才能这样做。因此,如果您尝试使用A作为类型实例化您的函数模板,那么得到编译错误,因为在事件 之后指定的名称将引用任何标准符合编译器上的非类型。

答案 2 :(得分:0)

因为编译器在实例化模板之前不知道T::iterator是什么,所以它不知道iterator是类变量,类型,函数还是什么。您需要使用typename告诉它它是一种类型。