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;
}