编译时:
#include <vector>
template<class T> class foo {
void bar() {
std::vector<T> x;
std::vector<T>::iterator i = x.begin();
}
};
int main() {
return 0;
}
我明白了:
# g++ ~test.cpp
test.cpp: In member function `void foo<T>::bar()':
test.cpp:7: error: expected `;' before "i"
不应该这样吗?
RHEL上的g ++版本3.4.3。
答案 0 :(得分:7)
你可以,但你需要告诉它iterator
有一种类型(它不知道,因为一般来说它可以取决于T
- 因为vector
是一个模板类型,理论上可以为某些T
提供特殊化,其中iterator
是一个函数或其他东西)。因此,您必须使用typename
来表明它始终是一种类型:
typename std::vector<T>::iterator i = x.begin();
答案 1 :(得分:1)
这应该做:
template<class T> class foo {
void bar() {
std::vector<T> x;
typename std::vector<T>::iterator i = x.begin();
}
};
我将引用IBM C ++编译器手册:
typename 关键字(仅限C ++)使用 关键字typename,如果你有 引用类型的限定名称 并取决于模板参数。 仅使用关键字typename 模板声明和定义。 以下示例说明了 使用关键字typename:
template<class T> class A { T::x(y); typedef char C; A::C d; }
语句 T :: x(y)不明确。它 可能是用函数调用函数x() 非局部参数y,或者它可以是一个 变量y的声明与类型 T :: x。 C ++将解释这一点 语句作为函数调用。为了 让编译器解释这个 声明作为声明,你会的 将关键字typename添加到 开始吧。声明 A :: C d; 是不正确的。 A类也指 到 A 因此取决于模板 参数。您必须添加关键字 typename到此开头 声明:
typename A :: C d; 您也可以使用 关键字 typename 代替 template参数中的关键字类 声明。
答案 2 :(得分:0)
如果不清楚编译器的其他含义是什么,不知道它是一个类型:在编译器解析模板foo时,它不知道你以后不会这样做:
namespace std {
template<>
class vector<int> {
int iterator(void);
};
}
然后实例化foo<int>
。那么vector<T>::iterator
将是一个函数,而不是一个类型,并且foo中的相关行应该无法解析。为了在没有帮助的情况下工作,编译器必须暂停解析foo直到它被实例化,并且他们找到了正确的类来确定'iterator'是类型表达式还是值表达式。我怀疑这可能导致循环依赖,但实施起来肯定会特别痛苦。因此,标准规定,除非声明,否则假定模板中依赖于参数的表达式不是类型。有两种(我认为)方式将它表示为一种类型,它们是(1)将它用作基类,(2)用typename限定它。
好的,所以在这个例子中你实际上不允许专门化std :: vector。而vector实际上有比我使用的模板参数更多的模板参数。所以在你的例子中,编译器在理论上可以假设比它更多。但是标准并没有特别规定语言依赖于命名空间std中的模板知识,因为(1)意图是实现可以在普通头中实现命名空间std,与编译器的任何其他头一样对待,(2)C ++应该被设计为语言+库,而不是“具有库的特殊语法语言”。事实上,(1)和(2)是相同的要求。