在说 Traits 之前, 需要先说一下 typedef. 我在大一的时候就被问过一个问题
1 | // 这两句有没有区别 |
答案当然是有, 但是我当时只能死记硬背的答出 typedef 不是简单的替换, 也不是改名, 而宏定义则仅仅是简单的文本替换. 但是这背后的原理并不是很清楚.
直到我读了 STL Source Code 发现其实现中 大量的 typedef, 然后不断比对后才得出了对它的新理解————typedef 可以将当前的类型进行「保存」.
例如将 vector 的定义简短化
1 | template <typename T...> |
看到了吗? template 里并不知道 T 的类型, 但是我通过 typedef 将它保存在一个叫 value_type 的元素中. 现在 value_type 便有了跟 T 一样的功能, 可以作为类型去定义新的变量.
那这用处在哪里呢? 我们要清楚, STL 的实现中, container 和 algorithm 是根本不知道对方的存在的, 而让他俩进行通信需要在中间加一个介质——iterator. 这样就出现了新的问题, algorithm 使用 itertator 操作 container, iterator 操作 container 的元素需要知道元素的类型 还要根据 container 的性质确定 iterator 的类型等等.
这时候就用到了 traits 技术, 实际上这可以说是一种编程策略, 利用模版技术获取类型, 上面的 vector 简短定义中我在 public 里用 typedef 将 vector 元素的类型 T 保存在了 value_type 里. 然后我只需要写一个东西将这个 value_type 给拿出来就可以了
在正式的说 traits 之前, 还要解释一下 typename (在《Effective C++》第 42 条比较清楚的介绍了它), typename 用作模版参数时, 与 class 没有什么区别, 但是在一些情况下(嵌套从属类别名称前) 需要加上 typename 关键字, 因为会存在一种情况, 例如 C::value_type 不是个类型, 而是个变量, 那用一个变量去定义一个变量就是语法错误了.
1 | template <class C> |
就像上面的代码这样, 然后我就得到了一个通用的 traits class(尽管实现为 struct)可以用来获取 container 的元素的类型,当然 STL 里 traits 的实现比这个复杂得多, 这只是举一个例子.
然后测试一下这个简易版 traits 的作用
1 | template <class C> |