- 论坛徽章:
- 2
|
不是高手,没办法用简洁的语言说清楚。。。 前面的例子可能会比较无聊,从第4步开始,可能会有你感兴趣的东西。
假设需要实现:
a. print_all 输出一个序列的所有元素。
b. maximum返回一个序列中的最大元素。
1. 手工实现各种print_all的版本
- void print_all(int const* first, int const* last) { for (; first!=last; ++first) cout<<*first<<" "; }
- void print_all(double const* first, double const* last) { for (; first!=last; ++first) cout<<*first<<" "; }
- void print_all(std:: vector<int>:: iterator first, std:: vector<int>:: iterator last) { for (; first!=last; ++first ) cout<<*first<<" "; }
复制代码 还有更多的、无限的组合等待程序员去实现。
2. 用template描述print_all的各种版本
- template<typename It>
- void print_all(It fisrt, It last) { for (; first!=last; ++first) cout<<*first<<" "; }
复制代码 只要某个类型的对象可以 —— i) first!=last, 相等测试) ii) ++first 自增 iii) *first 解引用 ix) cout<<*first 有输出的重载 —— 那它就可以使用这个模板。
上面的int const*、 double const*、 std::vector<int>::iterator 都满足这个条件。
还有更多的类型也满足这个条件。但只需要写print_all一次。
3. 手工实现maximum(为了简单,假设序列不允许为空)
- int maximum(int const* first, int const* last) {
- assert(first!=last);
- int y = *first;
- for (++first; first!=last; ++first) if (*first > y) y = *first;
- return y;
- }
复制代码 不会遇到什么问题,除了对每个类型都得重新写一次。
4. 用template描述maximum。 这与print_all就不同了, 嗯, 就会暴露出一些问题。
- template<typename It>
- /*这里该是什么*/ maximum(It first, It last) {
- assert(first!=last);
- /*这里又该是什么*/ y = *first;
- for (++first; first!=last; ++first) if (*first > y) y = *first;
- return y;
- }
复制代码 print_all没有这个问题是因为print_all里只产生了 *first 这个表达式。
编译器知道 *first 的类型, 然后使用cout<<*first的相应的重载版本。
而maximum需要写出那个表达式的类型。
5. 添加并使用编译时信息
编译时信息有很多,类型,嵌套类型,静态常量成员、嵌套枚举,类型能力。。。
这里需要的是嵌套类型。
- template<class It>
- typename It::value_type // 要求It类型必须有一个嵌套的类型定义,它是It解引用后的类型。
- maximum(It first, It last) {
- assert(first!=last);
- typename It::value_type y = *first;
- for (++first; first!=last; ++first) if (*first > y) y = *first;
- return y;
- }
复制代码 可以与std:: vector<int>:: iterator 协作,因为这个类型包含有嵌套的value_type类型定义。
但没法与int const* 协作, 因为这个类型不可能有嵌套类型定义。
6. 使用 iterator_traits
传说中的增加一个间接层就可以解决任何问题。
- template<typename It>
- typename std::iterator_traits<It>::value_type // 不直接问It,而是问std::iterator_traits<It>, It解引用的类型是什么。
- maximum(It first, It last) {
- assert(first!=last);
- typename std::iterator_traits<It>::value_type y = *first; // 同上
- for (++first; first!=last; ++first) if (*first > y) y = *first;
- return y;
- }
复制代码 std:: iterator_traits的定义是
- template <class Iterator>
- struct iterator_traits {
- typedef typename Iterator::value_type value_type;
- // ... 其他
- };
复制代码 std:: iterator_traits< std:: vector<int>:: iterator>:: value_type 就是 std:: vector<int>:: iterator:: value_type。
但std:: iterator_traits<int*> 就不同了, 因为std:: iterator_traits有个偏特化的定义:
- template<typename T>
- struct iterator_traits<T*> {
- typedef T value_type;
- // ... 其他
- };
复制代码 于是std:: iterator_traits<int*>:: value_type 就是 int。 于是上面的maximum 就ok了。
PS:
1. 这是STL的 iterator_traits 的目的
2. 其他traits是什么含义我不知道。 要知道一个术语流行开了就是混乱的开始。。。
很多人都在说traits, 但他们说的含义并不一定相同。
3. 无论术语如何, 总之template是一个编译时的行为。
类似运行时的函数add, 它需要两个参数才能完成其工作 int add(int a, int b) { return a+b; }
还有point, 使用point的人需要x以及y。
利用模板编程时也需要各种编译时的信息(前面已经举过例了)。 很多traits就是用各种方式提供这些信息。
4. maximum的问题在C++11里不存在, 因为有auto、 decltype、 alternative function syntax这些新增的东西。
我估计C++11可以解决maximum对编译时类型信息的需要, 但应该不能解决所有问题对编译时类型信息的需要。
估计还是会有各种traits。。。
|
|