条款47:请使用traits class表示类型信息
在stl的算法中,我们的希望往往是根据不同的迭代器类型进行不同的更有效率的操作:
1 template<typename IterT, typename DistT> 2 void advance(IterT iter, DistT dis) 3 { 4 if(iter is a random access iterator) 5 iter += dis; 6 else{ 7 if(dis >= 0){ 8 while(dis--) 9 iter++; 10 }else{ 11 while(dis++) 12 iter--; 13 } 14 } 15 };
这种可以识别Iterator类别的技术叫做iterator_traits,首先起要求每一个用户自定义的迭代器类型都应该有个typedef,类似下面这样:
1 template<...> 2 class list{ 3 public: 4 class iterator{ 5 typedef typename bidirectional_iterator_tag iterator_category; 6 ... 7 }; 8 };
这样iterator_traits就可以通过萃取技术将迭代器的类型给萃取出来:
1 template<typename IterT> 2 struct iterator_traits 3 { 4 typedef typename IterT::iterator_category iterator_category; 5 ... 6 };
上述的技术可能对指针来书的行不通的,因为指针无法内置的去指定iteartor_catagory;
只能通过模板的特化去解决这个问题:
1 tempalte<typename IterT> 2 struct iterator_traits<IterT *> 3 { 4 typedef random_access_iterator_tag iterator_category; 5 ... 6 };
有了iterator_traits技术以后,实现有效率的迭代就是可能的了。但是实际上不是通过 if Iterator is a random_access_iterator这种形式来实现的,而是通过函数的重载来实现的:
1 template<typename IterT, typename DistT> 2 doAdvance(IterT iter, DistT d, std::random_access_iterator_tag)//这里的tag往往先通过iterator_traits进行萃取而得到的; 3 { 4 iter += d; 5 } 6 template<typename IterT, typename DistT> 7 doAdvance(IterT iter, DistT d, std::bidirectional_iterator_tag) 8 { 9 if(dis >= 0){ 10 while(dis--) 11 iter++; 12 }else 13 while(dis++) 14 iter--; 15 } 16 tempalte<typename IterT, typename DistT> 17 doAdvance(IterT iter, DistT d, std::input_iterator_tag) //注意这里的迭代器版本 18 { 19 if(d < 0) 20 throw std::out_of_range("Negetive distance!"); 21 else{ 22 while(d--) ++iter; 23 } 24 }
上面最后一个版本的doAdvanced实际上是input_iterator版本,但由于forward_iterator_tag是继承与于input_iterator_tag的,所以这里的doAdvance同样可以对前向迭代器进行处理。
由上面的不同版本的iterator实际上就可以写出advance的实现版本了:
1 template<typename IterT, typename DistT> 2 doAdvance(IterT iter, DistT d) 3 { 4 doAdvance(iter, d, 5 typename std::iterator_traits<IterT>::iterator_category()); //注意这里使用到了typename 6 }
所以说一个traits class实际上相当于:
建立一组h重载函数或者是函数模板,彼此间的差异仅仅在于各自的traits参数。
建立一个控制函数或者模板函数,起调用上面的模板函数并且传递traits信息。
小结:
trait_class使得类型的相关信息在编译期就是可用的,其以template以及template的特例化来完成。
整合重载机制一起,traits_class可能可以在编译期就对类型执行if-else测试。