1. 程式人生 > >C++ 泛型演算法學習筆記(equal, accumulate, back_iterator, pair)

C++ 泛型演算法學習筆記(equal, accumulate, back_iterator, pair)

equal

equal是區間比較演算法
原型為:

template <class _InputIterator1, class _InputIterator2>
inline _LIBCPP_INLINE_VISIBILITY
bool
equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2)
{
    typedef typename iterator_traits<_InputIterator1>::value_type __v1;
    typedef
typename iterator_traits<_InputIterator2>::value_type __v2; return _VSTD::equal(__first1, __last1, __first2, __equal_to<__v1, __v2>()); } template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate> inline _LIBCPP_INLINE_VISIBILITY bool equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) { for
(; __first1 != __last1; ++__first1, (void) ++__first2) if (!__pred(*__first1, *__first2)) return false; return true; }

一般地,我們給equal傳入三個引數即可,前兩個分別是區間一的left迭代器和right迭代器,最後一個是區間二的left迭代器。從函式原型可以看出,區間二的範圍大於等於區間一才有可能得到true。
明白了這層原理,我們可以設計自己的資料型別,並自定義equal,比如:

struct _point{
    int
x; int y; _point():x(0), y(0) { } _point(int x_,int y_):x(x_), y(y_) { } friend _point operator + (const _point p1, const _point p2){ _point p(p1); p.x = p.x + p2.x; p.y = p.y + p2.y; return p; } friend ostream& operator << (ostream &out,const _point p){ out<<"("<<p.x<<" , "<<p.y<<")"; return out; } friend bool operator == (const _point &p1, const _point &p2){ return p1.x == p2.x && p1.y == p2.y; } }; int main() { _point p[] = {{0,0},{1,1},{2,2}}; cout<<equal(p,p+2,p)<<endl; return 0; }

或者使用lambda表示式定義其他的二元判斷符:

    cout<<equal(p+1,p+2,p+2,[](const _point p1,const _point p2){
        return p1.y && p2.y && p1.x/p1.y == p2.x/p2.y;
    })<<endl;

藉助equal,我們可以方便的判斷迴文字串,比如:

bool isPalindrome(const string str){
    return equal(str.begin(),str.begin()+str.size()/2,str.rbegin());
}

int main()
{
    cout<<isPalindrome("abcba")<<endl;
    cout<<isPalindrome("jordan")<<endl;
    return 0;
}

accumulate

accumulate函式定義在numeric標頭檔案中,用於計算一段範圍內的元素之和。
函式原型為:

template <class _InputIterator, class _Tp, class _BinaryOperation>
inline _LIBCPP_INLINE_VISIBILITY
_Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init, _BinaryOperation __binary_op)
{
    for (; __first != __last; ++__first)
        __init = __binary_op(__init, *__first);
    return __init;
}

在不傳入第四個引數的情況下,預設是使用二元操作符+。當我我們使用新的二元操作符後,返回值自然受到影響。

struct _point{
    int x;
    int y;
    _point():x(0), y(0) {  }
    _point(int x_,int y_):x(x_), y(y_) {  }
    friend _point operator + (const _point p1, const _point p2){
        _point p(p1);
        p.x = p.x + p2.x;
        p.y = p.y + p2.y;
        return p;
    }
    friend ostream& operator << (ostream &out,const _point p){
        out<<"("<<p.x<<" , "<<p.y<<")";
        return out;
    }
};

int main()
{
    vector<_point> vp;
    vp.push_back(_point(1,1));
    vp.push_back(_point(2,2));
    vp.push_back(_point(3,3));
    cout<<accumulate(vp.begin(),vp.end(),_point(0,0))<<endl;

    cout<<accumulate(vp.begin(),vp.end(),_point(0,0),[](const _point p1, const _point p2){
        _point p(p1);
        p.x = p.x - p2.x;
        p.y = p.y - p2.y;
        return p;
    })<<endl;
    return 0;
}

第二種計算使用了開發者lambda表示式定義的匿名二元函式。
注意:如果直接在結構體中定義operator -,然後再使用:cout<<accumulate(vp.begin(),vp.end(),_point(0,0), -)<<endl;
編譯器報錯,不認識這樣的表示式。

back_iterator

back_iterator返回物件的地址。

template <class _Container>
inline _LIBCPP_INLINE_VISIBILITY
back_insert_iterator<_Container>
back_inserter(_Container& __x)
{
    return back_insert_iterator<_Container>(__x);
}

// ==>

_LIBCPP_INLINE_VISIBILITY explicit back_insert_iterator(_Container& __x) : container(_VSTD::addressof(__x)) {}

// ==>

template <class _Tp>
inline _LIBCPP_NO_CFI _LIBCPP_INLINE_VISIBILITY
_Tp*
addressof(_Tp& __x) _NOEXCEPT
{
  return reinterpret_cast<_Tp *>(
      const_cast<char *>(&reinterpret_cast<const volatile char &>(__x)));
}

// operator = 呼叫了容器的push_back函式。

_LIBCPP_INLINE_VISIBILITY back_insert_iterator& operator=(const typename _Container::value_type& __value_)
        {container->push_back(__value_); return *this;}

整個過程:back_iterator將容器物件地址轉換成back_insert_iterator類的物件指標,通過這個物件指標呼叫=操作符,就能完成元素的插入。
應用:

    string str;
    str.push_back('1');
    auto it = back_inserter(str);
    *it = '2';
    cout<<str<<endl;  //12

操作符是一個特殊的函式,我們的*it = '2';還可以這樣寫:*it = (it.operator =('2'));
據此,我們還能將back_iterator和fill_n搭配使用,寫出另類的append函式:

    string str;
    str.push_back('1');
    fill_n(back_inserter(str),10,'0');
    cout<<str<<endl;  //10000000000

pair

查看了pair結構體的原始碼實現C++ pair原始碼,模擬它寫了一個三元組結構體treble:

template<typename T1, typename T2, typename T3>
struct treble {
    T1 first;
    T2 second;
    T3 third;
    treble(): first(),second(),third() {}
    treble(const T1 &__first, const T2 &__second, const T3 &__third){
        first = __first; second = __second; third = __third;
    }
    treble& operator = (const treble &another){
        this->first = another.first;
        this->second = another.second;
        this->third = another.third;
        return *this;
    }
    template <typename T>
    void basic_swap(T &t1, T &t2){
        T t = t1;
        t1 = t2;
        t2 = t;
    }
    void swap(treble &another){
        basic_swap(first,another.first);
        basic_swap(second,another.second);
        basic_swap(third,another.third);
    }
    friend ostream & operator <<(ostream &out, const treble &ins){
        out<<"("<<ins.first<<", "<<ins.second<<", "<<ins.third<<")";
        return out;
    }
};

template<typename T1, typename T2, typename T3>
inline _LIBCPP_INLINE_VISIBILITY
treble<T1,T2,T3>
make_treble(const T1 &__first, const T2 &__second, const T3 &__third)
{
    return treble<T1, T2, T3>(__first, __second, __third);
}

可以測試:

int main()
{
    treble<int,string,double> ins = make_treble(1,string("one"),1.00);
    cout<<ins<<endl;

    treble<int,string,double> ano;
    ano.swap(ins);
    cout<<"after swap:\n";
    cout<<ano<<endl;
    cout<<ins<<endl;
    return 0;
}