zoukankan      html  css  js  c++  java
  • c++:参数型别的推导

      STL源码剖析--侯捷

    • 总结

      尽管现在的很多语言支持参数类型的判别,但是c/c++并不支持这一特性。

      但是我们可以通过一些技巧使得c++具有自动判别参数类型的特性。

    • 模板

      我们都知道在模板类和模板函数中我们不用具体指定参数的型别,编译器会自动的判别参数的类型。

      所以我们想可不可以把编译器运行时所确定的型别萃取出来呢?

      可以通过内嵌型别实现。

    #include <iostream>
    using namespace std;
    
    template <class T>
    struct MyIter{
        typedef T value_type;
        //声明内嵌型别为value_type
        T* ptr;
        MyIter(T* p = 0):ptr(p){  }
        T& operator*()const{ return *ptr; }
    };
    
    template <class I>
    typename I::value_type
    //上面这一行告诉编译器这个型别,不然func无法返回  
    func(I ite)
    { return *ite; }
    
    int main(int argc, const char *argv[])
    {
        MyIter<int> ite(new int(8));
        cout << func(ite) << endl;
        //8
        return 0;
    }
    ~    

      这里的func函数就可以成功的把value_type萃取出来。

      但是这里还有一个隐蔽的陷阱。

      就是当我们这个迭代器是一个原生的指针时就会有问题,因为原生的指针并没有内嵌型别。

      这就需要这个模板针对这个类型写一个偏特化的版本。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 template <class T>
     5 struct MyIter{
     6     typedef T value_type;
     7     //声明内嵌型别为value_type
     8     T* ptr;
     9     MyIter(T* p = 0):ptr(p){  }
    10     T& operator*()const{ return *ptr; }
    11 };
    12 
    13 template <class I>
    14 struct iterator_traits_1{
    15   typedef typename I::value_type value_type;  
    16 };
    17 //对MyIter进行封装,获取它的内嵌型别
    18 
    19 template <class T>
    20 struct iterator_traits_1<T*>{
    21   typedef T value_type;  
    22 };
    23 //对原生指针进行型别的定义
    24 
    25 template <class I>
    26 typename iterator_traits_1<I>::value_type
    27 //告诉编译器下面函数返回的型别
    28 func(I ite)
    29 { return *ite; }
    30 
    31 int main(int argc, const char *argv[])
    32 {
    33     MyIter<int> ite(new int(8));
    34     cout << func(ite) << endl;
    35    //返回8 
    36     int a = 5;
    37     int *b = &a; 
    38     cout << func(b) << endl;
    39     //返回5
    40     return 0;
    41 }  

      这里我们对原来的类型MyIter又多了一层封装,这样的好处呢就是可以让下面的函数对原生指针也可以使用。

      为了可以使用原生指针,我们又写了一个偏特化的版本。

      特化版本和偏特化的版本的区别是,特化版本只是针对某一特定的类型实现。

      而偏特化版本是对于一组特定的类型特化,更具一般性。

      这里我们是针对所有的原生指针生成一个偏特化版本。

    • 关于traits

      traits在这里的作用是非常重要的,它所扮演的角色不仅仅是对各个类型的兼容,而且也是一个“类型萃取机”。

      需要说明的一点就是要想兼容traits,必须有相关的内嵌型别定义;这一点在STL的迭代器中至关重要。

    • iterator部分源码重列

      

     1 //STL全部迭代器类型                                                                                             
     2 struct input_iterator_tag {};
     3 struct output_iterator_tag {};
     4 struct forward_iterator_tag {} : public input_iterator_tag {};
     5 struct bidirectional_iterator_tag : public forward_iterator_tag {};
     6 struct random_access_iterator_tag : public bidirectional_iterator_tag {};
     7 
     8 template <class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>
     9 struct iterator{
    10     typedef Category      iterator_category;
    11     typedef T             value_type;
    12     typedef Distance      difference_type;
    13     typedef Pointer       pointer;
    14     typedef Reference     reference;
    15 };
    16 
    17 //类型萃取机traits
    18 template <class Iterator>
    19 struct iterator_traits{
    20     typedef typename Iterator::iterator_category  iterator_category;
    21     typedef typename Iterator::value_type         value_type;
    22     typedef typename Iterator::difference_type    defference_type;
    23     typedef typename Iterator::pointer            pointer;
    24     typedef typename Iterator::reference          reference;
    25 };
    26 
    27 //针对原生指针的偏特化版本
    28 template <class T>
    29 struct iterator_traits<T*>{
    30     typedef random_access_iterator_tag    iterator_category;
    31     typedef T                             value_type;
    32     typedef ptrdiff_t                     difference_type;
    33     typedef T*                            pointer;
    34     typedef T&                            reference;
    35 };
    36 
    37 //这个函数可以很方便的决定某个迭代器的类型(category)
    38 template <class Iterator>
    39 inline typename iterator_traits<Iterator>::iterator_category
    40 iterator_category(const Iterator&)
    41 {
    42     typedef typename iterator_traits<Iterator>::iterator_category category;
    43     return category();
    44 }
    45 
    46 //这个函数可以很方便的决定某个迭代器的distance_type
    47 template <class Iterator>
    48 inline typename iterator_traits<Iterator>::difference_type*
    49 distance_type(const Iterator&)
    50 {
    51     return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
    52 }
    53 
    54 //这个函数可以很方便的决定某个迭代器的value_type
    55 template <class Iterator>
    56 inline typename iterator_traits<Iterator>::value_type*
    57 value_type(const Iterator&)
    58 {
    59     return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
    60 }
    61 
    62 //以下是整组的distance函数
    63 //两个__distance函数中的第三个参数没有实际意义,仅仅是为了判别迭
    64 //代器类型
    65 template <class InputIterator>
    66 inline iterator_traits<InputIterator>::difference_type
    67 __distance(InputIterator first, InputIterator last, input_iterator_tag){
    68     iterator_traits<InputIterator>::difference_type n = 0;
    69     while(frist != last){
    70         ++first; ++n;
    71     }
    72     return n;
    73 }
    74 template <class RandomAccessIterator>
    75 inline iterator_traits<RandomAccessIterator>::difference_type
    76 __distance(RandomAccessIterator first, RandomAccessIteratorIterator last,
    77         random_access_iterator_tag){
    78     return last - first;
    79 }
    80 
    81 template <class InputIterator>
    82 inline iterator_traits<InputIterator>::difference_type
    83 distance(InputIterator first, InputIterator last)
    84 {
    85     typedef typename
    86         iterator_traits<InputIterator>::iterator_category category;
    87     return __distance(first, last, category());
    88 }                                                                                
    View Code

      关于这些特性,在STL大量的使用,一定程度上补充了c++的不足;

      最重要的是这些特性帮助我们在程序编译时就完成了类型的判断,而不是自己写个函数在运行时判断,这样做

      更快,更高效。

  • 相关阅读:
    java经典算法四十题
    使用windows Builder的一个中文乱码问题解决
    java经典算法四十题
    java经典算法四十题
    java经典算法四十题
    java经典算法四十题
    java经典算法四十题
    Redhat5下安装Oracle10g
    如何在DB2命令窗口执行sql脚本
    创建DB2跨库视图步骤
  • 原文地址:https://www.cnblogs.com/xujie-nm/p/4188644.html
Copyright © 2011-2022 走看看