STL中伪函数、函数对象(functor)初步理解(下)
来源:互联网 发布:c ide 知乎 编辑:程序博客网 时间:2024/05/22 13:48
好了,有了以上理论知识,就可以探讨一下函数对象的用法了,接下来我会以STL中使用最频繁的for_each做例子,来说明函数对象的原理和作用。
先查一下for_each的用法,在www.cplusplus.com上查到了比较详细的解释,原文如下:
Apply function to range
Applies function f to each of the elements in the range [first,last).The behavior of this template function is equivalent to:
template<class InputIterator, class Function> Function for_each(InputIterator first, InputIterator last, Function f) { for ( ; first!=last; ++first ) f(*first); return f; }
Parameters
- first, last
- Input iterators to the initial and final positions in a sequence. The range used is [first,last), which contains all the elements between first and last, including the element pointed by first but not the element pointed by last.
- f
- Unary function taking an element in the range as argument. This can either be a pointer to a function or an object whose class overloads operator().
Its return value, if any, is ignored.
Return value
The same as f.从上面这段文字首先可以看出,for_each是个区间操作函数,输入的前两个参数是起始迭代器和终点迭代器,而第三个参数则是一个function类型的参数,从下面的解释不难看出,这个参数既可以是函数,又可以是函数对象(也就是重载了()的类),也就是说,虽然函数对象在STL中很多地方已经取代了函数指针,但并不是函数指针就被完全抛弃了,因此,for_each所操作的对象有两种:函数对象和全局函数指针,以此类推,STL中所有其他range function也都具有相似的特征呢?很有可能,而事实上,几乎所有区间操作函数确实也全都是这样的。
好,有了以上的解释,我们就可以试试for_each的用法了。先从最简单的情况开始,试一试一元全局函数+函数指针的情况,看看以下代码片段。
#include <iostream> #include <vector> #include <iostream> #include <algorithm> using namespace std; void printTest(const int &data) { cout << data << endl; } int main() { int test[] = {1, 2 , 3 , 4 , 5}; vector<int> v(test, test + sizeof(test) / sizeof(int)); for_each(v.begin(), v.end(), printTest); return 0;
这个代码片段中,给for_each传的参数是全局函数指针,可以通过编译,这也说明了STL可以接受正确的函数指针作为参数,那么,可不可以把这个函数指针转化成functor?当然可以,而使用的方法正是之前提到的,用ptr_fun来转,也就是这样:
for_each(v.begin(), v.end(), ptr_fun(printTest));
view
仍然可以通过编译,也就是说,正确的全局函数指针转化成函数符是没有问题的。OK,下面试一下binary glocal function的情况,把原代码做一下微调,变成下面的样子:
#include <iostream> #include <vector> #include <iostream> #include <algorithm> using namespace std; void printTest(const int &data, const int &num) { int result = data+num; cout << "data:"<<data<<"num:"<<num<<",result:"<<result << endl; } int main() { int test[] = {1, 2 , 3 , 4 , 5}; vector<int> v(test, test + sizeof(test) / sizeof(int)); for_each(v.begin(), v.end(), printTest); return 0; }
w 变化的部分是printTest函数,由unary function变成了binary function,主函数不变,编译出错,出错的原因很简单,对于带两个形参的全局函数,如果直接把函数指针传给for_each,程序不会知道哪个形参应该接收变量,因此会出错,正确的做法应该是先绑定参数,然后再传给for_each即可,这里,就用到了STL里的参数绑定器,也就是bind1st和bind2nd,拿bind1st做例子,这个函数的声明是这样的:
template <class Operation, class T> binder1st<Operation> bind1st (const Operation& op, const T& x);
再看一下对于参数的描述:
op
Binary function object derived from binary_function.
x
Fixed value for the first parameter of op.
第一个参数op是我们需要的operation,而第二个参数则是需要绑定的参数的值,也就是说,按照这种方式,如果我想在上面的代码中调用bind1st应该是类似这种形式:bind1st(printTest , 3),但是很明显,直接这么用肯定是错的,因为printTest并不是一个继承自binary_function的函数,这里就用到了刚才所用到的ptr_fun了,这个函数可以把global function转化成一个函数对象,而对于有两个参数的函数对象来说,则会返回一个继承自binary_function的函数对象,因此,对于这个程序,正确的绑定应该是这样:bind1st(ptr_fun(printTest) , 3),在这个表达式里,printTest的第一个参数被绑定成了3,好,再次编译,因该没问题了吧?错了,仍然有问题,问题如下:
error C2535: 'void std::binder1st<_Fn2>::operator ()(const int &) const' : member function already defined or declared.
函数存在重复定义,而重复部分函数在STL中的xFunctional头文件中,重复的函数为:
std::pointer_to_binary_function<const int &,const int &,void,void (__cdecl *)(const int &,const int &)>
这里我们知道,ptr_fun可以把全局函数转换成一个pointer_to_binary_function,但是该函数的默认参数正式和printTest具有一样形式的函数,也就是说,两个函数的指针形式是一样的,因此定义重复,我们把原代码再次改一下,把printTest的声明改成这种形式:void printTest(const int &data, int num),再次编译、运行,产生的结果如下:
从截图可以清晰的看到,参数3被绑定到了printTest的第一个参数上,而第二个参数则会接收容器中的变量,该段代码产生的效果和手写循环一样。bind2nd的用法和bind1st一样,只不过它是把变量绑定到函数第二个参数上,假如用bind2nd(ptr_fun(printTest) , 3)代替上面的代码的话,会输出这样的结果:
从截图上可以看到,3被绑定到了第二个参数上。
讨论完了全局函数的情况,该讨论一下类中成员函数的情况了,其实成员函数和全局函数在转化成functor的原理上都是一样的,都是需要借用一个适配器,这个适配器可以让functor所需要的typrdef生效,而成员函数所需要的适配器则是mem_fun和mem_fun_ref,关于这两个函数的区别在上面已经提到,不做赘述,仅举一例来说明这个函数的用法。
在上个程序中,printTest是一个全局函数,可以被直接调用,现在,我们把它封装到一个类中,经过改造后的代码如下:
#include <iostream> #include <vector> #include <iostream> #include <algorithm> using namespace std; class print { private: int data; public: print(){} print(int _data) { data = _data; } void printTest(int num) { int result = data+num; cout <<"data: "<<data<<" num: "<<num<<" result: "<<result << endl; } }; int main() { print p1(1); print p2(2); print p3(3); vector<print> v; v.reserve(3); v.push_back(p1); v.push_back(p2); v.push_back(p3); for_each(v.begin(),v.end(),bind2nd(mem_fun_ref<void,print,int>(&print::printTest),3)); return 0; }
pla
那个for_each语句中,mem_fun_ref后面一连串的模板参数让人看起来有点晕,但是一点都不复杂,第一个参数是函数对象的返回类型,在这个程序里,printTest返回的是个void,第二个是调用函数的类,即是print类,第三个是绑定的参数类型,在printTest中就是int。之所以显式声明出模板参数,是为了演示需要,如果你愿意,你完全可以去掉哪些参数,而直接使用mem_fun_ref(&print::printTest),这是完全没有问题的,模板函数不必一直都显式指定参数类型。还有一点需要注意的就是,对于成员函数的变量绑定,肯定是用bind2nd绑定器的,因为默认状态下,第一个变量已经绑定给了调用函数的对象上,因此只有一个变量可以绑定,而mem_fun_ref也明确规定了转化的目标函数只能无参数或者具有一个参数。
函数对象和绑定器是密切相关的,本文对绑定器原理方面并没有做过多的叙述,如果各位有兴趣,可以自行搜索一下binder,以加深对函数对象的理解。
- STL中伪函数、函数对象(functor)初步理解(下)
- STL中伪函数、函数对象(functor)初步理解(上)
- STL仿函数functor
- STL学习笔记(四):仿函数(functor)
- C++ STL 学习 :for_each与仿函数(functor)(一)
- C++ STL 学习 :for_each与仿函数(functor)(一)
- C++ STL 学习 :更多仿函数(functor)(二)
- 函数对象(functor)
- 函数对象(functor)简介
- 函数对象 functor(整理)
- 深入理解仿函数(functor或function object)
- C++仿函数(functor)
- C++仿函数(functor)
- C++STL学习(9)仿函数(function objects, functor)
- stl中的仿函数functor的应用
- 全局函数与算子(Functor)
- C++ 仿函数(functor) (转载)
- 在STL中的functor(仿函数)的另一用处:当模板函数必须要进行偏特化时
- meta元素各属性的作用 - 小meta大作用
- jsp中防止页面传值乱码
- CB测试证书及IECEE-CB体系
- wxWidgets 中 wxIPV4address addr; 引发异常的原因
- MySQL日志把硬盘空间占完了?!
- STL中伪函数、函数对象(functor)初步理解(下)
- 如何修改framework层快速使其有效
- 解决:ClassWizard was unable to rename file...
- Objective-C 编程语言(2) 类,对象,消息 --- 运行时 对象
- 哈佛和冯诺依曼
- malloc(0)
- The Journey of ArchLinux Installation(续):常见问题及后期的安装配置
- API Demo SearchableDictionary代码分析一
- [教程]三步安装SSH,就怎么简单!