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是一个值,那么它将导致乘法。因此,单个模板对于两种不同的类型将具有不同的含义,这是不可接受的。“
答案 0 :(得分:41)
C ++中的名称可以涉及三个不同的实体层:值,类型和模板。
struct Foo
{
typedef int A;
static double B;
template <typename T> struct C;
};
三个名称Foo::A
,Foo::B
和Foo::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::A
和T::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
是否可转换为int
,a
和x
是否可以相乘,以及C<int>
是否确实具有成员函数gobble
都被推迟,直到您实际实例化模板。但是,名称表示值,类型或模板的规范是代码语法正确性的基础,必须在模板定义期间直接提供。
答案 1 :(得分:2)
因为在非模板foo中,您正在进行有效的乘法运算(假设您在使用之前声明了iter
)。尝试省略星号,您将收到编译器错误。 int隐藏了这个类。
typename关键字不阻止隐藏。只有gcc不正确地执行它才能这样做。因此,如果您尝试使用A
作为类型实例化您的函数模板,那么将得到编译错误,因为在事件 之后指定的名称将引用任何标准符合编译器上的非类型。
答案 2 :(得分:0)
因为编译器在实例化模板之前不知道T::iterator
是什么,所以它不知道iterator
是类变量,类型,函数还是什么。您需要使用typename
告诉它它是一种类型。