免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: solidmango
打印 上一主题 下一主题

[C++] 用最简洁的语言概括C++ traits(高手请进) [复制链接]

论坛徽章:
0
11 [报告]
发表于 2012-09-27 10:09 |只看该作者
STL源码,有折腾的必要么?
知道链表 vector map的原理了,就能适当做出最优化的代码写法选择了

论坛徽章:
0
12 [报告]
发表于 2012-09-27 12:46 |只看该作者
aychxm 发表于 2012-09-27 10:09
STL源码,有折腾的必要么?
知道链表 vector map的原理了,就能适当做出最优化的代码写法选择了


只能说人各有志,呵呵..

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
13 [报告]
发表于 2012-09-27 15:22 |只看该作者
不是高手,没办法用简洁的语言说清楚。。。 前面的例子可能会比较无聊,从第4步开始,可能会有你感兴趣的东西。


假设需要实现:
a. print_all 输出一个序列的所有元素。
b. maximum返回一个序列中的最大元素。


1.  手工实现各种print_all的版本

  1. void print_all(int const* first, int const* last) { for (; first!=last; ++first) cout<<*first<<" "; }
  2. void print_all(double const* first, double const* last) { for (; first!=last; ++first) cout<<*first<<" "; }
  3. void print_all(std:: vector<int>:: iterator first, std:: vector<int>:: iterator last) { for (; first!=last; ++first ) cout<<*first<<" "; }
复制代码
还有更多的、无限的组合等待程序员去实现。

2. 用template描述print_all的各种版本

  1. template<typename It>
  2. 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(为了简单,假设序列不允许为空)

  1. int maximum(int const* first, int const* last) {
  2.       assert(first!=last);
  3.       int y = *first;
  4.       for (++first; first!=last; ++first) if (*first > y) y = *first;
  5.       return y;
  6. }
复制代码
不会遇到什么问题,除了对每个类型都得重新写一次。


4. 用template描述maximum。 这与print_all就不同了, 嗯, 就会暴露出一些问题。

  1. template<typename It>
  2. /*这里该是什么*/ maximum(It first, It last) {
  3.       assert(first!=last);
  4.       /*这里又该是什么*/ y = *first;
  5.       for (++first; first!=last; ++first) if (*first > y) y = *first;
  6.       return y;
  7. }
复制代码
print_all没有这个问题是因为print_all里只产生了 *first 这个表达式。
编译器知道 *first 的类型, 然后使用cout<<*first的相应的重载版本。
而maximum需要写出那个表达式的类型。


5. 添加并使用编译时信息

编译时信息有很多,类型,嵌套类型,静态常量成员、嵌套枚举,类型能力。。。
这里需要的是嵌套类型。

  1. template<class It>
  2. typename It::value_type // 要求It类型必须有一个嵌套的类型定义,它是It解引用后的类型。
  3. maximum(It first, It last) {
  4.       assert(first!=last);
  5.       typename It::value_type y = *first;
  6.       for (++first; first!=last; ++first) if (*first > y) y = *first;
  7.       return y;
  8. }
复制代码
可以与std:: vector<int>:: iterator 协作,因为这个类型包含有嵌套的value_type类型定义。
但没法与int const* 协作, 因为这个类型不可能有嵌套类型定义。


6. 使用 iterator_traits

传说中的增加一个间接层就可以解决任何问题。

  1. template<typename It>
  2. typename std::iterator_traits<It>::value_type // 不直接问It,而是问std::iterator_traits<It>, It解引用的类型是什么。
  3. maximum(It first, It last) {
  4.       assert(first!=last);
  5.       typename std::iterator_traits<It>::value_type y = *first; // 同上
  6.       for (++first; first!=last; ++first) if (*first > y) y = *first;
  7.       return y;
  8. }
复制代码
std:: iterator_traits的定义是

  1. template <class Iterator>
  2. struct iterator_traits {
  3.       typedef typename Iterator::value_type        value_type;
  4.       // ... 其他
  5. };
复制代码
std:: iterator_traits< std:: vector<int>:: iterator>:: value_type 就是 std:: vector<int>:: iterator:: value_type。

但std:: iterator_traits<int*> 就不同了, 因为std:: iterator_traits有个偏特化的定义:

  1. template<typename T>
  2. struct iterator_traits<T*> {
  3.       typedef T value_type;
  4.       // ... 其他
  5. };
复制代码
于是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。。。

论坛徽章:
0
14 [报告]
发表于 2012-09-27 16:35 |只看该作者
OwnWaterloo 发表于 2012-09-27 15:22
不是高手,没办法用简洁的语言说清楚。。。 前面的例子可能会比较无聊,从第4步开始,可能会有你感兴趣的东 ...


谦虚啦,呵呵,您写的非常好,非常感谢..

我的总结是:
Traits就是特性的意思,就是说你告诉我一些东西,我能从你给我的东西里面提取出、计算出另外一些东西,而这些东西对于编译期是非常重要的,也就是“事物的特性”。

         traits
事物---------------->特性


         traits   
int *---------------->int



        

论坛徽章:
0
15 [报告]
发表于 2012-09-27 17:10 |只看该作者
本帖最后由 justmao945 于 2012-09-27 17:12 编辑

我觉得最简单的理解就是编译器使用template进行推导。。(trait本意是特征特性。。。不太明白翻译过来是啥,,)
比如c++11的is_xxx系列函数

  1. #include <iostream>
  2. #include <type_traits>

  3. class A {};

  4. int main()
  5. {
  6.     std::cout << std::boolalpha;
  7.     std::cout << std::is_array<A>::value << '\n';
  8.     std::cout << std::is_array<A[3]>::value << '\n';
  9.     std::cout << std::is_array<float>::value << '\n';
  10.     std::cout << std::is_array<int>::value << '\n';
  11.     std::cout << std::is_array<int[3]>::value << '\n';
  12. }
复制代码
其实很简单。。看下STL代码就知道什么是traits了。。当然ls那个大神已经举了个例子了~~

论坛徽章:
0
16 [报告]
发表于 2012-10-08 07:43 |只看该作者
回复 15# justmao945


    非常感谢..

论坛徽章:
0
17 [报告]
发表于 2012-10-08 12:33 |只看该作者
非高手进来看下        

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
18 [报告]
发表于 2012-10-08 12:56 |只看该作者
难个毛, 模板+模板特化就是traits, 再配合重载机制包装上wrapper就是STL的核心技术了.

论坛徽章:
0
19 [报告]
发表于 2012-10-08 21:16 |只看该作者
回复 18# linux_c_py_php


    不解啊,还是实例来得清楚,什么样的重载?难道是我的知识学杂了?

论坛徽章:
0
20 [报告]
发表于 2012-10-09 04:04 |只看该作者
回复 18# linux_c_py_php

看起来这位兄台没有读过 MCD


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP