[Boolan] C++第九周 STL 泛型編程(三)

1. 迭代器

STL的算法的操作對象都是迭代器,所以就需要迭代器提供算法需要的各種信息,例如每個迭代器都需要定義的5個typedef
template<typename _Tp>
struct _List_iterator
{
    typedef std::bidirectional_iterator_tag    iterator_category;       //容器的類別
    typedef _Tp     value_type;     //容器的元素的類型
    typedef _Tp*    pointer;    
    typedef _Tp&    reference;  
    typedef ptrdiff_t   difference_type;        //位置差類型
};

其中容器類型iterator_category分爲5中

struct input_iterator_tag {};   
struct output_iterator_tag {};
struct forward_iterator_tag       : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};

標準庫並沒有使用枚舉或者define來定義iterator_category,而是使用了結構體,爲什麼,我覺得可以從下面的樣例上獲得一點經驗

容器類型的樣例:

//
//  week09_category.cpp
//  Boolan
//
//  Created by 張峯 on 2017/3/13.
//  Copyright © 2017年 張峯. All rights reserved.
//

#include <iostream>
#include <array>
#include <string>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <iterator>
#include <unordered_map>
#include <typeinfo>

using namespace std;

void _display_category(random_access_iterator_tag) {
    cout << "   "  << "random_access_iterator_tag" <<endl;
}
void _display_category(bidirectional_iterator_tag) {
    cout << "   "  << "bidirectional_iterator_tag" <<endl;
}
void _display_category(forward_iterator_tag) {
    cout << "   "  << "forward_iterator_tag" <<endl;
}

void _display_category(output_iterator_tag) {
    cout << "   " << "output_iterator_tag" <<endl;
}

void _display_category(input_iterator_tag) {
    cout << "   "  << "input_iterator_tag" <<endl;
}

template<typename T>
void display_category(string str, T itr)
{
    cout << str;
    typename iterator_traits<T>::iterator_category cagy;
    _display_category(cagy);
    cout << "typeid(itr).name() = " <<typeid(itr).name()<<endl << endl;;
}

int main(int argc, char *argv[])
{
    display_category("array", array<int, 10>::iterator());      //array   random_access_iterator_tag
    display_category("vecotr", vector<int>::iterator());    //vecotr   random_access_iterator_tag
    display_category("forward_list", forward_list<int>::iterator());    //forward_list   forward_iterator_tag
    display_category("deque", deque<int>::iterator());  //deque   random_access_iterator_tag

    display_category("set", set<int>::iterator());      //set   bidirectional_iterator_tag
    display_category("map", map<int, int>::iterator()); //map   bidirectional_iterator_tag
    display_category("unordered_set", unordered_set<int>::iterator());  //unordered_set   forward_iterator_tag
    display_category("unordered_map", unordered_map<int, int>::iterator()); //unordered_map   forward_iterator_tag

    display_category("istream", istream_iterator<int>());       //istream   input_iterator_tag
    display_category("ostream", ostream_iterator<int>(cout, ""));   //ostream   output_iterator_tag

    return 0;
}

///////////////////////////////////////////////
array   random_access_iterator_tag
typeid(itr).name() = Pi

vecotr   random_access_iterator_tag
typeid(itr).name() = NSt3__111__wrap_iterIPiEE

forward_list   forward_iterator_tag
typeid(itr).name() = NSt3__123__forward_list_iteratorIPNS_19__forward_list_nodeIiPvEEEE

deque   random_access_iterator_tag
typeid(itr).name() = NSt3__116__deque_iteratorIiPiRiPS1_lLl1024EEE

set   bidirectional_iterator_tag
typeid(itr).name() = NSt3__121__tree_const_iteratorIiPNS_11__tree_nodeIiPvEElEE

map   bidirectional_iterator_tag
typeid(itr).name() = NSt3__114__map_iteratorINS_15__tree_iteratorINS_12__value_typeIiiEEPNS_11__tree_nodeIS3_PvEElEEEE

unordered_set   forward_iterator_tag
typeid(itr).name() = NSt3__121__hash_const_iteratorIPNS_11__hash_nodeIiPvEEEE

unordered_map   forward_iterator_tag
typeid(itr).name() = NSt3__119__hash_map_iteratorINS_15__hash_iteratorIPNS_11__hash_nodeINS_17__hash_value_typeIiiEEPvEEEEEE

istream   input_iterator_tag
typeid(itr).name() = NSt3__116istream_iteratorIicNS_11char_traitsIcEElEE

ostream   output_iterator_tag
typeid(itr).name() = NSt3__116ostream_iteratorIicNS_11char_traitsIcEEEE

2. 算法與traits的聯繫

在之前的的第七週的時候介紹過traints, 再簡單的回顧一下, traits總共有5個typedef,主要用於告知算法,容器當前的各種信息; 
算法根據traits的信息進行偏特化,進行鍼對性的計算,提高程序效率
template <typename _Tp>
{
    iterator_category;    容器的類別
    value_type;
    pointer;
    reference;
    difference_type;    
}

算法針對traits的信息進行模版偏特化

上圖可以清晰看到,算法_advance通過容器類型的繼承關係,以及模版的偏特化,來根據各個容器的特性計算距離,提高效率

3. 算法

函數模版

在C++標準庫中,算法通常是使用函數模版來實現的,並且爲了能夠實現自定義數據類型的計算,通常會預留一個位置,用來傳遞自定義數據類型的計算
這個預留位置,可以是函數指針或者仿函數

下列代碼就是說明了這一點,可以通過sort驗證,sort充分利用了函數模版的參數自動推導這一特性,
以及模版的泛化和特化,是的編譯器能夠自動識別該使用那個版本,
並且通過仿函數或者函數指針增加了擴展性
/*
template <class _RandomAccessIterator, class _Compare>
void sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp);

template <class _RandomAccessIterator>
void sort(_RandomAccessIterator __first, _RandomAccessIterator __last);

template <class _Tp>
void sort(_Tp** __first, _Tp** __last);

template <class _Tp>
void sort(__wrap_iter<_Tp*> __first, __wrap_iter<_Tp*> __last);

template <class _Tp, class _Compare>
void sort(__wrap_iter<_Tp*> __first, __wrap_iter<_Tp*> __last, _Compare __comp);
*/


#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

bool myfunc(int i, int j) {
    return (i < j);
}

struct myclass{
    bool operator() (int i, int j) {
        return (i < j);
    }
}myObjFunc;

int main(int argc, char *argv[])
{
    int myints[] = {32, 71, 12, 45, 26, 80, 57, 93};
    vector<int> myvec(myints, myints+8);

    sort(myvec.begin(), myvec.end());

    sort(myvec.begin(), myvec.end(), myfunc);

    sort(myvec.begin(), myvec.end(), myObjFunc);

    return 0;
}

仿函數

在C++中,有些算法是通過仿函數來實現的,這些仿函數基本都會繼承某個類的,例如binary_function<T,T,T> 或者unarg_function<T,T>
爲什麼會這樣? 這是因爲這些仿函數並不是單獨存在的,他們可能只是算法的一部分,需要其他算法進行調用,才能發揮作用
但是如果是的自己可以被其他函數調用,就需要告知調用者  自己的返回值,參數等特性
這一點跟容器的traits非常類似,把需要告知算法的信息進行統一的封裝
template <class Arg, class Result>
struct unarg_function{
    typedef    Arg    argument_type;
    typedef    Result    result_type;
};

template <class Arg1, class Arg2, class Result>
struct binarg_function {
    typedef    Arg1    first_argument_type;
    typedef    Arg2    second_argument_type;
    typedef    Result    result_type;
};

適配器

函數適配器bind2nd

調用實例 count_if(vi.begin(), vi.end(), bind2nd(less<int>(), 40));

源碼解析:
    1. less<int>()   創建了一個仿函數對象,這個仿函數繼承自binarg_function
    2. bind2nd(less<int(), 40>)
        這個編譯器通過函數模版的參數類型自識別,可以確定__Operation的類型是int,
        由此可以確定了 binder2nd這個仿函數對象的類型,
        然後生成一個臨時對象,把less<int>的的仿函數對象放到op這個成員變量中,40放到value中存起來
        那binder2nd是如何知道第二個參數的類型的?
            這就是less繼承binarg_fiunction的功勞
            因爲less繼承了binarg_function,所以binder2nd就可以通過它所定義的typedef來得知less第一個參數和第二個參數的類型
            確定類型之後,就可以定義成員變量了
        需要注意的是此時bind2nd只是返回了一個臨時的仿函數對象,裏面存着less和他的第二個參數
    3. 通過看count_if的源碼可以得知我們把bind2nd創建的臨時仿函數對象放到了_pred這個形參中
        並且通過__pred(*__first)這個函數調用到了binder2nd的操作符重載,把40當作第二個參數傳遞進去

    這裏要注意一個細節, 就是binder2nd也即成了unarg_function這個表示一元函數的父類,
    這樣binder2nd纔可以繼續被其他的算法或者仿函數進行有效的調用

    當時在看這段的是時候,最讓人拍案的地方是通過編譯器針對函數模版的類型自識別來自動生成binder2nd模版的類實例
    當時是感覺一環扣一環,缺一不可,剛剛好

//less 
template <class _Tp>
struct less : binary_function<_Tp, _Tp, bool>
{ 
    bool operator()(const _Tp& __x, const _Tp& __y) const
        {return __x < __y;}
};

//  bind2nd  source
template <class __Operation, class _Tp>
binder2nd<__Operation>
bind2nd(const __Operation& __op, const _Tp& __x) {
    return binder2nd<__Operation>(__op, __x);
}


template <class __Operation>
class binder2nd
    : public unary_function<typename __Operation::first_argument_type,
                            typename __Operation::result_type>
{
protected:
    __Operation                                op;
    typename __Operation::second_argument_type value;
public:
    binder2nd(const __Operation& __x, const typename __Operation::second_argument_type __y)
        : op(__x), value(__y) {}
    typename __Operation::result_type operator() 
        ( typename __Operation::first_argument_type& __x) const
            {return op(__x, value);}
    typename __Operation::result_type operator()
        (const typename __Operation::first_argument_type& __x) const
            {return op(__x, value);}
};



template <class _InputIterator, class _Predicate>
typename iterator_traits<_InputIterator>::difference_type
count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred)
{
    typename iterator_traits<_InputIterator>::difference_type __r(0);
    for (; __first != __last; ++__first)
        if (__pred(*__first))
            ++__r;
    return __r;
}

inserter適配器

下圖中心思想:
    通過inserter適配器的包裹,把foo的insert操作,自動分配內存,適配到inseter重載的=號操作上去

inserter適配器

X 適配器

ostream_iterator

istream_iterator

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章