如下:
template<class T>
class Widget; //使用”class”
template<typename T>
class Widget; //使用”typename”
虽然使用了不同的关键字,但从C++的角度来说,声明模板参数时,关键字class 和typename意义完全相同。 然而C++并不把class和typename视为等价。有时你一定得使用typename。
考虑下面一个函数模板,接受一个STL兼容容器为参数,容器内持有对象可赋值为ints。假设这个函数仅仅是打印其第二元素值。(这个函数无意义,只是为了论述)
template<typename C>
void print2(const C& container) //打印容器第二元素
{
if(container.size()>2)
{
C::const_iterator it(container.begin()); //第一个元素迭代器
++it;
int value = *it;
std::cout<<value;
}
}
上述代码强调两个局部变量,val 和it 。it类型为C::const_itertor,实际类型取决于模版参数C。模版内出现的名称如果相依于某个模版参数,称之为从属名(depedent names)。如果该名称在class内嵌套,则称嵌套从属名称(nested dependent name),如C::const_iterator(嵌套从属类型)。嵌套从属名称可能会导致解析困难,如下:
template<typename C>
void print2(const C& container) //打印容器第二元素
{
C::const_iterator * x;
…
}
似乎是声明一个指针x,其类型为C::const_itertor(这是在我们已经知道它是个类型的情况下)。 但如果C::const_itertor不是个类型呢?如C刚好有个static成员变量而刚好命名为const_iterator,或x碰巧是个全局变量名?在这种情况下上面情况也许就不是声明一个局部变量,而是一个相乘的动作。 很明显,在知道C之前,无从知道C::const_iterator是否为类型。C++规则:如果解析在模版中遇到一个嵌套从属名称,它便假设这个名称不是类型。所以在缺省情况下,在缺省情况下嵌套从属名不是类型。再看:
template<typename C>
void print2(const C& container) //打印容器第二元素
{
if(container.size()>2)
{
C::const_iterator it(container.begin()); //该名被假设为非类型….
}
}
很显然上述代码不合法,it只有在C::const_iterator声明为类型时才合理,正确的做法是:
template<typename C>
void print2(const C& container) //打印容器第二元素
{
if(container.size()>2)
{
typename C::const_iterator it(container.begin()); //该名被假设为非类型….
}
}
一般规则为:想在模板中涉及一个嵌套从属类型名,就必须在其前面加个类型关键字typename。
例外情况:typename只用来验明嵌套从属类型名称,其他名不该有它存在。如下函数模版接受一个容器和一个“指向该容器”的迭代器
template<typename C> //可使用class或typename
void f(const C& container //不能使用typename
typename C::iteratorit); //一定使用typename
上述C并不是嵌套从属类型名,(它并不嵌套于任何“取决于template参数”的东西内),所以声明container并不需要以typename为前导,而C::iterator必须以typename为前导。 “typename必须做为嵌套从属类型前缀”这一规则的例外是:typename不可以出现在基类列表内的嵌套从属类型前,也不可在成员初始列中作为基类修饰符。如:
template<typename T>
class Derived: public Base<T>::Nested //基类列表中不允许typename
{
public:
explicitDerived(int x)
:Base<T>::Nested(x) //成员初始值列,不允许typename
{
typenameBase<T>::Nested tmp; //嵌套从属类型名称
//不在基类列表也不在成员初始值列中,作为基类修饰符必须加上typename
…
}
};
再看一个例子:
template<typename IterT>
void workWithIterator(IterT iter) {
typenamestd::iterator_traits<IterT>::value_type tmp(*iter);
…..
}
typename std::iterator_traits<IterT>::value_type,相当于说“类型为IterT的对象所指之物的类型”。这个语句声明一个局部变量,使用IterT的对象所指的类型,将tmp初始化为iter所指物。比如IterT类型是vector<int>::itertor,则tmp类型就是int.是vector<string>::iterator,tmp类型就为string。由于std::iterator_traits<IterT>::value_type是个嵌套从属类型,故在其之前放置typename。 当然,这个定义太长,我们可以使用typedef,如下:
template<typename IterT>
void workWithIterator(IterT iter) {
typedef typenamestd::iterator_traits<IterT>::value_type value_type;
value_type tmp(*iter);
…..
}
由于typename的相关规则在不同编译器上有不同实现,故在移植性方面也会有小问题。
需要记住的:
1、声明模版参数时,关键字class和typename可互换
2、使用typename标志嵌套从属类型名。但不得在基类列表和成员初始值列内以它做为基类的修饰符。
网友评论