模板类型参数
在泛型编程中,typename 和 class 都可以作为模板类型参数,是一样的。
template<class T> //等同于 template<typename T>
class Animal {
public:
T name;
};
在模板中标识嵌套依赖类型名称
规则:不管是在模板函数还是模板类中,如果有使用 嵌套依赖类型名称 nested dependent type name 的话,都需要在他的前面加上 typename。
先不管概念,直接从实际出发看看 typename 发挥了什么作用:
class Cat {
public:
class Color{};
using Weight = int; typedef int Age; //和Color一样,属于内部类型,在模板类中作为类型使用前面要加上 typename
};
template<class T>
class Animal {
public:
T::Color* p1; //(1) ×
typename T::Color* p2; //(2) √
};
int main(){
Animal<Cat> ani;
}
在模板类 Animal 中的代码 (1),T::Color* CatColor
看起来是声明一个 T::Color
指针类型的对象,然而在编译器的眼里却不是这样的。模板函数/类的代码在编译过程中,并不知道模板类型参数是什么,要等到实际运行了才知道。这就导致了编译器不知道 T::Color
是类型还是类变量。如果 T::Color
是类变量,p1
是一个全局变量,这条语句就是乘法*
操作了。
这时候就需要用到 typename
了,将他放到嵌套依赖类型名称的前面,就能显式告诉编译器,这是一个类型,而不是变量,从而正确实现代码功能,比如上述代码中的 (2)。
模板类中,可以使用模板类型参数本身,也可以使用参数内部定义的类,如:using Weight = int
和 typedef int Age
定义的类都可以在模板类中使用。
如果没有加上 typename
关键字:
默认情况下,C++ 语言假定通过作用域运算符访问的名字不是类型。
不能使用 typename 的地方
typename
可以出现在函数体内,可以出现在函数返回类型前,可以出现在函数参数前。但是当子类继承的父类是嵌套依赖类型名称的时候,typename
不能出现在类的基类列表以及成员初始化列表中作为父类的修饰符。
template<class T>
class Animal {
};
//Size方法是模板类型参数T的内部类
template <typename T>
class Cat : public Animal<T>::Size{ //Animal<T>::Size 前不可用 typename 修饰
public:
Cat(int m) : Animal<T>::Size(m){ //Animal<T>::Size 前不可用 typename 修饰
//函数体中可以使用
}
};
个人认为,嵌套依赖类型名称在子类的父类列表中表示父类,在成员初始化列表中表示类的构造函数,都是类型,没有歧义,所以也就不需要 typename
。
网上关于 typename 的文章绝大部分都是出自 Effective C++ 中的条款 42。书中关于相关概念有如下描述:
一个 template(模板)中的依赖于一个 template parameter(模板参数)的名字被称为 dependent names(依赖名字)。当一个 dependent names(依赖名字)嵌套在一个 class(类)的内部时,我称它为 nested dependent name(嵌套依赖名字)。一个涉及到一个 type(类型)的 nested dependent name(嵌套依赖名字)就是 nested dependent type name(嵌套依赖类型名)。
又是依赖又是嵌套的,这么绕口,想了很久没想明白,书中也没有具体展开,也没所谓了。
只要知道这个就行了:嵌套依赖类型名称指的是,在模板类中使用的模板类型参数 T 的内部类 C。(C是Y的嵌套依赖类型名称)