for_each的尝试
template<class InputIter, class T1, class T2> T2 for_each(InputIter first, InputIter last, T1 f) { for (; first != last; ++first) f(*first); return f; }
在上面的for_each算法代码中,模版类型T1是函数f的一个类型,但在C++语言中,变量有类型,函数并没有类型,因此不能找到一个具体的C++类型来具现模板类型T1,这样繁华的算法实现是行不通的。
由此,自然会提出函数对象的编程模式,用一个结构体来封装函数,内部提供operator()操作符函数,支持传统函数形式的调用。如此,既解决了函数类型的问题,又可以继续使用f()形式的函数调用。与函数指针相比较,使用函数对象调用函数的速度更快,并且可拥有多个函数对象的实例。函数对象的一般定义如下,关键在内部要定义一个"()"操作符。
struct f { void operator()(T x) {......} // 定义operator()函数,使f(T x)形式有意义 };
如下是SGI C++ STL提供的for_each算法代码。模板类型T1用Function代替,表明是一个函数对象,删去模板类型T2,返回函数对象。
template <class InputIter, class Function> Function for_each(InputIter first, InputIter last, Function f) { for (; first != last; ++first) f(*first); return f; }
for_each的实例
#include <iostream> #include <algorithm> using namespace std; struct print { void operator()(int a, int b) { printf("%d\n", a + b)}; // 定义operator()函数,使f(T x)形式有意义 }; int main() { int a[] = {1,2,3,4,5,6}; const int len = sizeof(a) / sizeof(int); for_each(a, a +len, print()); return 0; }
为了方面对容器的元素进行基本的算术逻辑运算,C++ STL定义了一些常用的函数对象,供算法调用。这些函数对象都是相应二元关系函数的一个封装,从结构体binary_function继承过来。
binary_function结构体的作用是,提供参数类型和返回值类型的信息,以便在必要时可将二元函数对象转换为一元函数对象。如下是SGI C++ STL在stl_function.h文件提供的函数对象。
template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; template <class T> struct plus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x +y; } }; template minus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x - y; } };
适配器是一种泛化的模板类型。通过传递一个具现的C++类型给适配器的模板类型参数, 就可以生成一种新的类型,从而在已有类型的基础上完成新类型的构造。
适配器本身是一个新的自定义类型 class/struct,其中会包含一个或多个辅助的所适配的类型的成员,并对内含的成员的接口进行改造,再以新的适配器类型向外部提供接口。
适配器分为迭代器适配器、函数对象适配器和容器适配器。
一般而言,容器是以 class templates 完成,算法以 function templates 完成,仿函数是一种将 operator() 重载的 class template, 迭代器则是一种将 operator++ 和 operator* 等指针习惯常行为重载的 class template。
C++ STL的反向迭代器reverse_iterator提供字符串的尾端开始查找字符。reverse_iterator用类而不是结构体来实现,是由于内部需要用protected来控制变量的访问权限。
逆向迭代器并不改变存储中的实际位置,只是改变了逻辑位置。逆向迭代器 reverse_iterator 就是对 正向迭代器 iterator 的二次包装,在保持和利用正向迭代器的原有行为的同时,将一个正向迭代器转换为逆向迭代器,实现了逻辑上的相反遍历的功能。
template <class Iterator> class reverse_iterator { protected: Iterator current; public: // 使用关键字typename说明其后标识符是一个类型,而不是类的变量,进而用typedef重定义位一个规范的类型名字 typedef typename iterator_traits<Iterator>::iterator_category iterator_category; typedef typename iterator_traits<Iterator>::value_type value_type; typedef typename iterator_traits<Iterator>::diffetence_type difference_type; typedef typename iterator_traits<Iterator>::pointer pointer; typedef typename iterator_traits<Iterator>::reference reference; typedef Iterator iterator_type; typedef reverse_iterator<Iterator> Self; public: reverse_iterator{} 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; } Self& operatpr++() { // 实际执行"--" --current; return *this; } Self operator++(int) { Self tmp = *this; --current; return tmp; } Self& operatpr--() { // 实际执行"++" ++current; return *this; } Self operator--(int) { Self tmp = *this; ++current; return tmp; } Self operator+(difference_type n) const { return Self(current - n); } Self operator+=(difference_type n) { current -= n; return *this; } Self operator-(difference_type n) const { return Self(current + n); } Self operator-=(difference_type n) { current += n; return *this; } reference operator[](difference_type n) const { return *(*this + n); } };
reverse_iterator例子
#include <iterator> #include <iostream> #include <vector> int main(void) { using namespace std; vector<int> v; v.push_back(3); v.push_back(6); v.push_back(9); reverse_iterator<vector<int>::iterator, int> rfirst(v.end()); reverse_iterator<vector<int>::iterator, int> rend(v.begin()); while(rfirst != rend) { cout << *rfirst << end; ++rfirst; } return 0; }
下面再介绍一种迭代器适配器insert_iterator。与reverse_iterator不同,这个插入迭代器并不适用iterator来做模板类型,而是使用一个容器Container* container。insert_iterator可直接利用容器的插入函数,提供一种进行"="赋值插入方式。
对于 insert iterator,可以将一般迭代器的赋值(assign)操作,转化为插入(insert)操作。
insert_iterator的实现思路为:每个 insert_iterators 内部都维护一个由用户指定的容器和容器相应的迭代器;当客户端对 insert iterators 做赋值操作时,就在 insert_iterators 的 operator= 操作符中调用底层容器的 insert() 函数。对于 insert iterators 的前进、后退、取值、成员取用等操作都是关闭的,或是不允许的。
template <class Container> class insert_iterator { protected: Container* container; typename Container::iterator iter; public: typedef Container container_type; // 包含一个容器 typedef output_iterator_tag iterator_category; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; insert_iterator(Container& x, typename Container::iterator i) : container(&x), iter(i) {} insert_iterator<Container&> operator=(const typename Container::value_type& value) { iter = container->insert(iter, value); // 调用容器的insert()函数插入数据 ++iter; // 继续移动迭代器 return *this; } insert_iterator<Container>& operator*() { return *this; } insert_iterator<Container>& operator++() { return *this; } //实际并没有移动 insert_iterator<Container>& operator++(int) { return *this; } //实际并没有移动 };
insert_iterator例子
List<int> L; L.push_back(3); insert_iterator<List<int>> ii(L, L.begin()); *ii++ = 0; *ii++ = 1; *ii++ = 2;
一些在算法中调用的函数对象,要求是一个返回bool值的一元函数对象,以使容器的每一个元素可以被这个一元函数对象进行判断处理。从下面的查找算法find_if的代码,可以看到代码中的函数对象pred,必须是一个返回bool值的单参数函数对象。
template<class InputIter, class Predicate> // 返回bool值的函数对象,称为谓词判断Predicate InputIter find_if(InputIter first, InputIter last, Predicate pred, input_iterator_tag) { while(first != last && !pred(*first)) // 要求pred是返回bool值的一元函数(对象) ++first; return first; }
一个返回bool值的函数对象,称为谓词判断Predicate,如greater、less、not_equal_to等基本的函数对象。
函数对象适配器的作用在于,将双参数的函数对象转化为单参数的函数对象,以适应算法的调用要求。
传递某个值给二元函数对象,也就是所谓的绑定,C++ STL提供了两种用于绑定的函数对象适配器:binder1st/bind1st和binder2nd/bind2nd,分别将数值绑定二元函数的第一个参数和第二个参数。
binder1st/bind1st、binder2st/bind2st的实现:
//一元函数结构 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; //参数类型,可以理解为x对应的类型 typedef Result result_type;//返回值类型,可以理解为 z 对应的类型 }; //二元函数结构 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; //第一个参数类型,可以理解为x对应的类型 typedef Arg2 second_argument_type;//第二个参数类型,可以理解为y对应的类型 typedef Result result_type; //返回值类型,可以理解为 z 对应的类型 }; // unary_function 是一元函数结构。Operation是两个参数的函数对象,binder1st是一个单参数对象,类型是Operation的第二参数的类型。 template <class Operation> class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type> { protected: Operation op; // 用于绑定二元函数对象,提供op()函数处理 typename Operation::first_argument_type value; // first_argument_type表示第一个参数类型,可以理解为x对应的类型 public: // y是指需要绑定的常数值 binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {} // operator()的返回值类型,调用op() typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const { return op(value, x); // value绑定到Operation第一个参数,x用作第二个参数 } }; template<class Operation, class T> inline binder1st<Operation> bind1st(const Operation& fn, const T& x) { typedef typename Operation::first_argument_type Arg1_type; return binder1st<Operation>(fn, Arg1_type(x)); } template <class Operation> class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; // 用于绑定二元函数对象,提供op()函数处理 typename Operation::second_argument_type value; public: // y是指需要绑定的常数值 binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} // operator()的返回值类型,调用op() typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(value, x); // value绑定到Operation第一个参数,x用作第二个参数 } }; template<class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& fn, const T& x) { typedef typename Operation::second_argument_type Arg2_type; return binder2nd<Operation>(fn, Arg2_type(x)); }
参考文档:https://www.cnblogs.com/chuyongliu/p/5308616.html
实例如下
#include <iostream> #include <algorithm> // count_if #include <functional> // binder #include <list> using namespace std; int main() { // Data int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; list<int> aList(iarray, iarray + 10); // binder和bind区别: // 1. 类绑定器有binder1st和binder2nd,而函数绑定器是bind1st和bind2nd // 2. bind是一个全局的模板函数其返回值为一个binder模板类的实例 // 3. binder要指定泛型 int k = count_if(aList.begin(), aList.end(), binder1st<greater<int>>(greater<int>(), 5)); // bind1st bind2nd功能比较 // k = count_if(aList.begin(), aList.end(), bind1st(greater<int>(), 5)); // bind1st(greater<int>(), 5); //---->5 > x 即5作为第一个固定参数。返回5大于的数字的个数 // bind2nd(greater<int>(), 5); //---->x > 5 即5作为第二个固定参。返回大于5的数字的个数 cout << k << endl; system("pause"); return 0; }