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;
}
上图可以清晰看到,算法_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重载的=号操作上去