STL源码剖析(四)
functors
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator()
,这个类就有了类似函数的行为,就是一个仿函数类了。
在STL中,将仿函数主要分为了三大类:算术类(Arithmetic)、逻辑运算类(Logical)和相对关系类(Relational)。
算术类(Arithmetic)举例
template <class T>
struct plus : public binary_function<T, T, T>
{
T operator()(const T& x, const T& y) const
{ return x + y; }
};
template <class T>
struct minus : public binary_function<T, T, T>
{
T operator()(const T& x, const T& y) const
{ return x - y; }
};
...
逻辑运算类(Logical)举例
template <class T>
struct logical_and : public binary_function<T, T, bool>
{
bool operator()(const T& x, const T& y) const
{ return x && y; }
};
...
相对关系类(Relational)
template <class T>
struct equal_to : public binary_function<T, T, bool>
{
bool operator()(const T& x, const T& y) const
{ return x == y; }
};
template <class T>
struct less : public binary_function<T, T, bool>
{
bool operator()(const T& x, const T& y) const
{ return x < y; }
};
...
通过上面的代码可以发现,functors都继承了一个基类。STL规定每个 Adaptable Function 都应该挑选合适的父类继承,因为 Adaptable Function 将会提问一些问题。
template <class Arg, class Result>
struct unary_function
{
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class Result>
struct binary_function
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
比如上面的less
通过继承binary_function
拥有了3个typedef
,这些typedef
可能会在一些适配器中被询问到,详见后面适配器中的源码。
Adapters
Adapters 相当于一种修饰的作用,在容器、迭代器和仿函数的基础上,对其进行一种改造。将改造完成后的容器、迭代器或仿函数交给用户使用,但其核心还是通过内部的容器、迭代器和仿函数进行工作。所以就存在: Container Adapters, Iterator Adapters 和 Functor Adapters三类。
容器适配器:stack, queue
stack
template <class T, class Sequence=deque<T>>
class stack{
...
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
proctected:
Sequence c;// 底层容器
public:
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop { c.pop_back(); }
};
queue
template <class T, class Sequence=deque<T>>
class queue{
...
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
proctected:
Sequence c;// 底层容器
public:
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
const_reference front() const { return c.front(); }
reference back() { return c.back(); }
const_reference back() const { return c.bakc(); }
void push(const value_tyoe& x) { c.push_back(x); }
void pop { c.pop_back(); }
};
函数适配器:binder2nd, not1
对于这样一行语句:
cout << count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40));
首先需要注意的是less<int>()
这并不是函数的调用,而是生成一个less<int>
的对象!
count_if
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 n = 0;
for(; first != last; ++first)
if(pred(*first))
++n;
return n;
}
bind2nd
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op,const T& x)
{
typedef typename Operator::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));//返回一个binder2nd<Operation>对象!
}
binder2nd
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()(const typename Operation::first_argument_type& x) const
{
return op(x,value);//这里才是函数的调用
}
};
在这些代码中可以看到适配器在询问仿函数一些问题,这些问题就是仿函数继承的基类中的typedef
。所有能回答出这些问题的仿函数都称为 Adaptable Function。
not1
template <class Predicate>
inline unary_negate<Predicate> not1(consat Predicate& pred)
{
return unary_negate<Predicate>(pred);
}
template <class Predicate>
class unary_negate : public unart_function<typename Predicate::argument_type, bool>
{
protected:
Predicate pred;
public:
eplicit unary_negate(const Predicate& x) : pred(x) {}
bool operator()(const typename Predicate::argument_type& x) const
{
return !pred(x);
}
};
新型适配器,bind
bind 使用例子(摘自cplusplus网站)
#include <iostream> // std::cout
#include <functional> // std::bind
// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}
struct MyPair {
double a,b;
double multiply() {return a*b;}
};
int main () {
using namespace std::placeholders; // adds visibility of _1, _2, _3,...
// binding functions:
auto fn_five = std::bind (my_divide,10,2); // returns 10/2
std::cout << fn_five() << '\n'; // 5
auto fn_half = std::bind (my_divide,_1,2); // returns x/2
std::cout << fn_half(10) << '\n'; // 5
auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x
std::cout << fn_invert(10,2) << '\n'; // 0.2
auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y)
std::cout << fn_rounding(10,3) << '\n'; // 3
MyPair ten_two {10,2};
// binding members: member function 其实有个 argument: this
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
std::cout << bound_member_fn(ten_two) << '\n'; // 20
auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
std::cout << bound_member_data() << '\n'; // 10
return 0;
}
std::bind 可以绑定:
- functions
- function objects
- member functions, _1 必须是某个object地址
- data members, _ 必须是某个object地址
所以可以现在可以用bind
替换bind2nd
,改写如下:
vector<int> v {15,37,94,50,73,58,28,98};
int n = count_if(v.cbegin(), v.cend(), not1(bind2nd(less<int>, 50)));
cout << "n=" << n << endl;//5
vector<int> v {15,37,94,50,73,58,28,98};
int n = count_if(v.cbegin(), v.cend(), not1(bind(less<int>, _1, 50)));
cout << "n=" << n << endl;//5
迭代器适配器 reverse_iterator, inserter
reverse_iterator:用来实现去反向指针rbegin(), rend()的实现。
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { returun reverse_iterator(begin()); }
template <class Iterator>
class reverse_iterator
{
protected:
Iterator current;//对应的正向迭代器
public:
//逆向迭代器的5中 associated types 都和对应的正向迭代器相同
typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
typedef typename iterator_traits<Iterator>::value_type value_type;
...
typedef Iterator iterator_type; // 表示正向迭代器
typedef reverse_iterator<Iterator> self;// 表示反向迭代器
public:
explicit reverse_iterator(iterator_type x) : current(x) {}
reverse_iterator(const self& x) : current(x.current) {}
iterator_type base() const { return current; }
reference operator*() const
{ //关键所在! 对于逆向迭代器的取值,就是将正向的迭代器退一位取值。
Iterator tmp = current;
return *--tmp;
}
pointer operator->() const { return &(operator*()); }
//前进便后退,后退便前进
self& operator++() { --current; return *this; }
self& operator--() { ++current; return *this; }
slef operator+(difference_type n) const { return self(current - n); }
slef operator-(difference_type n) const { return self(current + n); }
};
inserter: 将iteartor中的复制操作改为插入操作,并且将iteartor右移一个位子。可以让用户执行表面上assign而实际上insert的行为。
template <class Container>
class insert_iterator
{
protected:
Container* container;
typename Container::iterator iter;
pbulic:
typedef output_iterator_tag iterator_category;
insert_iterator(Container& x, typename Container::iterator i) : container(&x), iter(i) {}
//对赋值操作符重载,以实现插入
insert_iterator<Contain>& operator=(const typename Container::value_type& value)
{
iter = container->insert(iter, value);
++iter;
return *thisl
}
};
//辅助函数,帮助用户使用insert_iterator
template <class Container, class Iterator>
inline insert_iterator<Container> inserter(Container& x, Iterator i)
{
typedef typename Container::iterator iter;
return insert_iterator<Container>(x, iter(i));
}
特殊适配器 ostream_iterator, istream_iterator
ostream_iterator
先来看一个例子
#include <iostream> //std::cout
#include <iterator> //std::ostream_iterator
#include <vector> //std::vector
#include <algorithm> //std::copy
int main()
{
std::vector<int> myvector;
for (int i = 1; i < 10; ++i) myvector.push_back(i * 10);
std::ostream_iterator<int> out_it(std::cout, ",");
std::copy(myvector.begin(), muvector.end(), out_it);
return 0;
}
输出结果:
10,20,30,40,50,60,70,80,90,
首先来看看copy
这个函数做了什么
template<class InputIterator first, InputIterator last,OutputIterator result>
copy(InputIterator first,InputIterator last, OutputIterator result)
{
while(first != last)
{
*result = *first;
++result;
++first;
}
}
再对比着ostream_iterator
的源码,就能分析出输出的原因:
template<class T,class chatT=char,class traits=char_traits<charT>>
class ostream_iterator:public iterator<output_iterator_tag,void,void,void,void>
{
basic_ostream<charT,traits>* out_stream;
const charT* delim;
public:
typedef charT char_type;
typedef traits traits_type;
typedef basic_ostream<charT,traits> ostream_type;
ostream_iterator(ostream_type& s):out_stream(&s),delim(0){}
ostream_iterator(onstream_type&s, const charT* delimiter):out_stream(&s),delim(delimiter){}
ostream_iterator(const ostream_iterator<T,charT,traits>& x):ostream_iterator(x.out_stream),delim(x.delim){}
~ostream_iterator(){}
ostream_iterator<T,chatT,traits>& operator=(const T& value){//关键点!!
*out_stream << value;
if(delim!=0)
*out_stream << delim;
return *this;
}
ostream_iterator<T,charT,traits>& operator*(){return *this;}
ostream_iterator<T,charT,traits>& operator++(){return *this;}
ostream_iterator<T,charT,traits>& operator++(int){return *this;}
};
关键就在于重载了=
号运算符。
istream_iterator
还是先看一个例子
#include <iostream>
#include <iterator>
int main()
{
double value1, value2;
std::cout << "Please, insert two values:";
std::istream_iterator<double> eos;
std::istream_iterator<double> iit(std::cin);//当创建对象时,就已经在要求输入了
if (iit != eos)
value1 = *iit;
++iit;
if (iit != eos)
value2 = *iit;
std::cout << value1 << "*" << value2 << "=" << (value1*value2) << "\n";
return 0;
}
这个例子就是一个简单的乘法,其中std::istream_iterator<double> iit(std::cin);
相当于cin >> value;
。具体的原理还是看源码吧。
template <class T, class charT = char, class traits = char_traits<charT>>, class Distance = ptrdiff_t >
class istream_iterator : public iterator<input_iterator_tag, T, Distance, const T*, const T&>
{
basic_istream<charT, traits>* instream;
T value;
public:
typedef charT char_type;
typedef traits traits_type;
typedef basic_istream<charT, traits> istream_type;
istream_iterator() :instream(0) {}
istream_iterator(istream_type& s) :in_stream(&s) { ++*this; }
istream_iterator(const istream_iterator) < T, charT, traits, Distance > & x):in_stream(x.in_stream), value(x.value){}
~istream_itetator() {}
const T& operator*() const { return value; }
const T* operator->() const { return value; }
istream_iterator<T, charT, traits, Distance>& operator++() {
if (in_stream && !(*in_stream >> value))
in_stream = 0;
return *this;
}
istream_iterator<T,charT,traits,Distance>operator++(int) {
istream_iterator<T, charT, traits, Distance> tmp = *this;
++*this;
return tmp;
}
};
对照源码可以发现,std::istream_iterator<double> iit(std::cin);
这里调用了istream_iterator的++符号,此时就已经开始输入了。