C++模版typename的双重意义

来源:互联网 发布:淘宝二手镜头店铺推荐 编辑:程序博客网 时间:2024/06/04 23:30

我们都知道在定义模版的时候 “模版头部”类型参数列表中不仅可以使用关键字class也可以使用关键词typename,看如下代码:

#include <iostream>template <class T>                  //template关键字表示将定义一个模版T kMax(const T& t1,const T& t2){    return t1>t2?t1:t2;}int main(void){    int Num = kMax(10,20);    //调用具象化后的模版函数    std::cout << "#:" << Num << std::endl;    return 0;}
定义一个模版就必须有模版头部 其中 template关键词是定义模版必须的,<>尖括号中的class也是一个关键字 用于表示后面的T是一个通用类型。

可以这么理解,关键字class相当于一般定义变量时候的“类型” 如int ,而T则相当于该类型定义的“变量”,只是这个“变量”用于存放类型,调用所定义的函数模版的时候,编译器会将调用时指定的类型(用户指定或用户不指定的话编译器将通过调用函数时提供的参数类型得到 当然使用类模版的时候就必须自己提供 否者编译出错)

其中<>尖括号中的class关键字也可以使用typename这个关键字代替 如:template <typename T> 在定义模版的时候这个typename和class 俩关键字是等价的,也就是说用哪个都行,但使用class可能会让人以为在具象化模版的时候只能指定类类型,而其他类型则不能指定,其实并不是这样的,上面也说了是通用类型,所以什么类型都可以,而使用typename这个关键字就很明显了 让人一看就知道 哦 T所代表的是类型 什么类型都可以指定 而不会让人产生误解 所以如果可以的话 尽量还是使用typename替代class关键字,为什么说“如果可以”呢,因为可能有些旧式编译器不支持这个关键字 那就只能使用class了。

另外typename关键字还可以用于解决“嵌套从属名称”的解析困难问题上,看以下代码:

template <typename T>void Func(const T& temp){     T::iterator iter(temp.begin());     ++iter;     int value = *iter;     std::cout << value << std::endl;}
该函数只是一个打印迭代器所指向的值,并没什么实际意义,但是可以从中知道 iter是一个T::iterator类型,但实际上是什么,必须取决于模版参数T,模版中出现的名称如果相依于模版参数 如T,则称之为“从属名称”,如果从属的名称在class内呈现嵌套状,我们称之为“嵌套从属名称”。其中T::iterator就是“嵌套从属名称” 也称之为“嵌套从属类型名称”。还有一个名称value他是一个int类型,因为不是相依于模版参数,所以称之为“非从属名称”。而“嵌套从属名称”可能会导致解析困难的问题,假如有如下代码:

template <typename T>void Func(const T& temp){     T::iterator* iter;     /*     ...........     */}
看起来像是我们声明一个T::iterator类型的指针iter,之所以会这么认为,是因为我们“已经知道”T::iterator是个类型,但是如果不是一个类型呢?如果T是一个名称空间,而iterator是该名称空间中的一个全局变量,又或者碰巧T中有一个静态变量iterator,那么上面语句将不是声明一个局部变量,而是一个相乘动作,iter又没有被定义,则编译器就会报错了。

在我们知道T是什么之前,没有任何办法知道T::iterator是一个类型,当编译器开始解析该模版时,尚未知道C是什么东西。C++中有一个规则可以解析该歧义状态:当解析器在模版中遇到“嵌套从属名称”的时候,就会假设该名称不是一个类型,除非你告诉它是一个类型,否者编译器就可能会因此拒绝编译。

那么如何告诉编译器 它 是一个类型呢?看如下代码:

template <typename T>void Func(const T& temp){     typename T::iterator iter;   //加上typename     /*     ...........     */}
也就是在“嵌套从属名称”语句前面加上“typename”关键字,这就是该关键字的第二种用法,当添加这个关键字后,编译器就会认为该T::iterator是一个类型了。

一般的,如果模版中有“嵌套从属名称”就应该在它前面加上typename关键字,然后一个例外情况就是,如果基类列表或成员初始化列表中带有“嵌套从属名称”则不能添加该关键字进行修饰。如下代码:

template <typename T>class Derived : public Base<T>::World {   //不允许使用typenamepublic:    Derived(int x):Base<T>::World{        //不允许使用typename        typename Base<T>::World temp;     //允许使用typename    }   };
最后,typename只能用于“嵌套从属名称”上面 而不能用做“从属名称”上面。

总结:

1.声明模版参数的时候,前缀关键字class和typename可互换,但是最好使用typename。

2.要用typename关键字修饰“嵌套从属类型名称”,但不能将其用于继承时的基类列表和成员初始化列表中。

原创粉丝点击