看了很多遍,还是记录一下
1、在模板声明时typename和class是等价的
template<class T> class Widget; // uses "class"
template<typename T> class Widget; // uses "typename"
两面的两句等价
2、什么情况下必须放typename?
template<typename C>
void print2nd(const C& container)
{
C::const_iterator *x; //①
...
}
此时的编译器会纠结:const_iterator是一个什么东西?const_iterator是模板参数C内的一个static变量?x是一个全局变量?
再或者:const_iterator是模板参数C内部的一个typedef。假设模板参数C最后用Class_C来实例化,Class_C类似这样:
class Class_C{
typedef int const_iterator;
...
};
那上面的例子中的语句①就是声明一个指向int类型的指针x,语句①转化过来就是这样:
int *x;
程序员是不能让编译器纠结的,编译器纠结的后果是:报错。程序员嘛,就debug吧。
所以此时我们应该告诉编译器const_iterator是模板参数C里面的一个类型。即在C的前面加上typename就可以了,像这样:
template<typename C>
void print2nd(const C& container)
{
typename C::const_iterator * x; //①
...
}
3、总结一下上面的用法
a、模板内出现的名称如果依赖于某个模板参数,称之为从属名称(dependent name)。如果从属名称在class内呈嵌套状,我们称为嵌套从属名称(nested dependent name)。距离如下:
template<typename C>
void print(const C & container)
{
C::const_iterator iter(container.begin());
cout << *iter << endl;
int value = *iter;
return;
}
- 在上述代码中,我的理解C就是一个从属名称。假如我们在函数被使用了类似 C inst_c; 这样的语句,inst_c就是从属于C;
- 同理,value的类型是语言内置类型,不依赖于任何模板参数,因此被称为 非从属名称;
- C++编译器在面对从属名称时,如果此时该从属名称又嵌套了其他类型,如此处的 iter就是C::const_iterator类型,这里的C::const_iterator 称为嵌套从属类型名称(嵌套于C类型,从属于模板参数C)。
b、再来看一个例子:
template<typename C> // typename allowed (as is "class")
void f(const C& container, // typename not allowed
typename C::iterator iter); // typename required
上述的C并不是嵌套从属类型名称 (它并非嵌套与任何“取决于模板参数”的东西内),所以声明container时并不需要以typename为前导。
但C::iterator是个嵌套从属类型名称,所以必须以typename为前导。
c、说人话
使用某个模板类里面typedef的类型时,如果这个类型与模板参数C相关,就需要使用typename。
即这样的形式:
y(C)::iterator it;
这样的语句前面就需要前置typename;
参考:
[1] https://blog.csdn.net/gatieme/article/details/50946005
[2] https://blog.csdn.net/xuqingict/article/details/24884811
[3] C++模板沉思录(上)