C++ typedef & typename知识点总结
用过 C++ 的同学对 typename 和 typedef 相信并不是很陌生,但是当我看到下面这段代码的时候仍然无法理解:
| |
按理来说 typedef 一般不是用来定义一种类型的别名,如下:
定义了一个 int 的别名是 SpeedType,那么我就可以这样用:
但是 typedef 后面接 typename 表示什么意思呢?typename 不是用来定义模板参数的吗?下面我们分别归纳一下 typedef & typename 的用法。
首先来看看 typedef 的几种常见用法。
为特定含义的类型取别名
这个我在上面已经讲过了,但是它是定义一种类型的别名,而不只是简单的宏替换,也可以用作同时声明指针型的多个对象的。
比如:
本来想要把 pa、pb 两个变量都声明为字符串,但是这样只能成功声明了一个。但是我们使用 typedef 就可以成功声明两个:
为结构体取别名
在声明变量的时候,需要带上 struct,即像下面这样使用:
用来定义与平台无关的类型
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
| |
在不支持 long double 的平台二上,改为:
当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
typename
typename 关键字用于引入一个模板参数,这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名:
typename 在这里的意思表明 T 是一个类型。如果没有它的话,在某些情况下会出现模棱两可的情况,比如下面这种情况:
作者想定义一个指针iter,它指向的类型是包含在类作用域T中的iterator。可能存在这样一个包含iterator类型的结构:
那么 foo<ContainsAType>(); 这样用的是时候确实可以知道 iter是一个ContainsAType::iterator类型的指针。但是T::iterator实际上可以是以下三种中的任何一种类型:
- 静态数据成员
- 静态成员函数
- 嵌套类型
所以如果是下面这样的情况:
那 T::iterator * iter;被编译器实例化为ContainsAnotherType::iterator * iter;,变成了一个静态数据成员乘以 iter ,这样编译器会找不到另一个变量 iter 的定义 。所以为了避免这样的歧义,我们加上 typename,表示 T::iterator 一定要是个类型才行。
得出结论
我们回到一开始的例子,对于 vector::size_type,我们可以知道:
vector::size_type是vector的嵌套类型定义,其实际等价于 size_t类型。
| |
那么这个例子的真是面目是,typedef创建了存在类型的别名,而typename告诉编译器std::vector<T>::size_type是一个类型而不是一个成员。
加一个例子
我们下面来讲解一个例子,用模板实现类似下面的循环:
首先我们需要一个循环模板:
| |
这里应该可以看的懂,这几个模板,无论是 res_type 还是 type 都用 typename 修饰,表明都是类型,然后再接上 typedef 表示给这个类型定义了一个别名。
再定义循环模板的时候,有一个约定,它必须提供一个静态数据成员,cond_value,及两个子类型定义,res_type 和 next_type:
- cond_value 代表循环的条件(真或假),表明直接是一个确定的 bool 类型的静态数据成员,没有用
typename修饰; - res_type 代表退出循环时的状态,是一个类型而不是一个成员;
- next_type 代表下面循环执行一次时的状态,是一个类型而不是一个成员;
WhileLoop 用特化来决定走递归分支还是退出循环分支。
然后我们定义一个模板代表数值:
通过 value 可以获取到对应的数值,value_type 则是这个数值的类型。
| |
通过上面的模板可以实现 While<Sum<10>::type>::type::value 1 加到 10 的结果。实际上就是通过类型的循环展开实现了 1 加到 10 运算结果。
Reference
https://feihu.me/blog/2014/the-origin-and-usage-of-typename/
https://www.cnblogs.com/charley_yang/archive/2010/12/15/1907384.html