template<class T>
class Set
{
public:
void insert(const T& item);
void remove(const T& item);
private:
std::list<T> rep;
}
template<typename T>
void Set<T>::remove(const T& item)
{
typename std::list<T>::iterator it = // question here
std::find(rep.begin(),rep.end(),itme);
if(it!=rep.end()) rep.erase(it);
}
为什么需要remove()中的typename?
答案 0 :(得分:21)
一般来说,C ++需要typename
,因为它从C继承了不幸的语法[*],如果没有非本地信息就可以说 - 例如 - 在A * B;
A
命名一个类型(在这种情况下,这是B
的声明作为它的指针)或不是(在这种情况下,这是一个乘法表达式 - 很可能自A
以来,对于没有非本地信息的所有人,可以是一个重载operator*
来做一些奇怪事情的类的实例; - )。
在大多数情况下,编译器确实具有消除歧义所需的非本地信息(尽管不幸的语法仍然意味着低级解析器需要来自保留符号表信息的更高级别层的反馈)...但是模板它没有(一般情况下,虽然在这种特定情况下,专门化std::list<T>
在技术上可能是非法的,因此其::iterator
不是类型名称; - )。
[*]不仅仅是我的观点,还有Ken Thompson和Rob Pikes的观点,他们正在忙着设计和实现一种新的内部使用的编程语言:这种新的编程语言,而其语法主要是C-like,不重复C的语法设计错误 - 它是新语言(例如在旧的Pascal中),语法足以区分必须命名类型的标识符和不必的类型; - )。
答案 1 :(得分:12)
如果您正在谈论与typename
一起使用的std::list<T>::iterator
:
typename用于说明iterator
是类std::list<T>
中定义的类型。
如果没有typename,std::list<T>::iterator
将被视为静态成员。只要依赖于模板参数的名称是类型,就会使用typename
。
答案 2 :(得分:1)
我认为通常你需要类声明中的typename / class T以及函数定义,因为你可以定义函数定义的完整/部分模板规范。 IE浏览器。你可以将你的删除函数专门用于整数,字符串,无论它是什么。因此,在这种情况下,您告诉编译器“这是任何类型的通用模板”,然后您可以定义仅为整数指定的相同函数。
答案 3 :(得分:1)
typename,因为否则编译器不知道它是一个类型声明而不是表达式。
根据this page,“如果您具有引用类型且取决于模板参数的限定名称,请使用关键字typename。”