此代码无法编译:
template <typename T>
struct B {
typedef T type;
};
struct D0 : public B<int> {
void h(type) { }
};
template <typename T>
struct D : public B<T> {
void f(typename B<T>::type) { }
void g(type) { }
};
具体来说,尽管编译D0::h(type)
和D::f(typename B<T>::type)
,但D::g(type)
却没有。为什么type
中无法看到D
?
答案 0 :(得分:4)
答案可以在两个地方找到。第一个C ++ 11标准草案N3337
:
§14.6.2/ 3
在类模板的定义或类模板的成员中, 如果类模板的基类依赖于模板参数, 在非限定名称查找期间不检查基类范围 无论是在类模板或成员的定义点还是 在类模板或成员的实例化期间。 [示例:
typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // a has type double };
A
定义中的类型名称X<T>
绑定到typedef 在全局命名空间范围中定义的名称,而不是typedef名称 在基类B<T>
中定义。 - 结束示例]
(大胆强调我的)
其次,parashift FAQ在Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?中提供了一个人类可读的解释:
也许令人惊讶的是,以下代码无效C ++,尽管如此 一些编译器接受它:
template<typename T> class B { public: class Xyz { /*...*/ }; // Type nested in class B<T> typedef int Pqr; // Type nested in class B<T> }; template<typename T> class D : public B<T> { public: void g() { Xyz x; // Bad (even though some compilers erroneously (temporarily?) accept it) Pqr y; // Bad (even though some compilers erroneously (temporarily?) accept it) } };
这可能会伤害你的头脑;如果你坐下来会更好。
在
D<T>::g()
中,名称Xyz
和Pqr
不依赖于模板参数 T,因此它们被称为非独立名称。另一方面,B<T>
取决于模板参数T
,因此B<T>
称为依赖项 名。这是规则:编译器不查看依赖的基类 (例如
B<T>
)查找非依赖名称(例如Xyz
或Pqr
)。作为一个 结果,编译器不知道它们甚至存在,更不用说了 类型。此时,程序员有时会在其前面添加
B<T>::
,例如:template<typename T> class D : public B<T> { public: void g() { B<T>::Xyz x; // Bad (even though some compilers erroneously (temporarily?) accept it) B<T>::Pqr y; // Bad (even though some compilers erroneously (temporarily?) accept it) } };
不幸的是,这也不起作用,因为那些名字(是你 准备?你坐下了吗?)不一定是类型。 “嗯?!?”你 说。 “不是类型?!?”你惊叹道。 “太疯狂了;任何傻瓜都可以看到他们 是类型;看看!!!“你抗议。对不起,事实是他们 可能不是类型。原因是可以有专业化 例如
B<T>
,B<Foo>
,其中B<Foo>::Xyz
是数据成员。 由于这种潜在的专业化,编译器无法假设B<T>::Xyz
是一种类型,直到它知道T
。解决方案是给予 通过typename
关键字编译提示:template<typename T> class D : public B<T> { public: void g() { typename B<T>::Xyz x; // Good typename B<T>::Pqr y; // Good } };
答案 1 :(得分:1)
显然,这是由于编译器的语义编译阶段的顺序。碰巧的是,对于编译器的视图,符号type
不存在于任何范围内,因为它还没有(尚未)实例化模板,因此它本身没有实例化D
。 / p>
唯一的解决方法是强制编译器在语义编译的后期阶段解析符号,例如使用...
void f(typename B<T>::type) {}
或者,如果派生类不是模板,但基类是,则编译器强制首先解析/实例化/基类,因此避免在派生类中出现任何可能的错误。
但是,如果 base和derived都是模板,则不是这种情况,因为编译器将在所有已经实例化的范围(例如全局范围)中查找派生符号 实例化派生模板类之前,如果它失败了,你已经知道int main(){printf("%d\n", argc);}
会发生什么,不是吗?