STL中伪函数、函数对象(functor)初步理解(下)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

好了,有了以上理论知识,就可以探讨一下函数对象的用法了,接下来我会以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;  
}  

变化的部分是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,以加深对函数对象的理解。

C++ STL函数对象实战:从原理到性能优化,掌握算法核心 在C++编程中,函数对象Functor)是实现算法泛化与性能优化的关键技术。其核心原理是通过重载operator()运算符,使类实例具备函数调用行为,从而在编译期实现类型安全的多态。相较于函数指针,函数对象的核心优势在于可携带状态、支持编译器内联优化,并能无缝融入模板系统,这为STL算法提供了极高的灵活性和运行效率。在工程实践中,函数对象广泛应用于自定义排序、数据过滤、序列变换等场景,是连接数据与操作的智能粘合剂。本文通过性能调优等实战案例,深入解析函数对象与Lambda表达式、std::function 阅读详情

相关推荐

C++函数对象与Lambda表达式:从基础原理到STL实战应用

在C++编程中,可调用对象是实现灵活行为抽象的核心概念。函数对象通过重载operator()实现,本质上是具有状态的类实例,为STL算法提供了可定制的操作单元。其技术价值在于将行为封装为对象,支持状态保持和值语义传递,广泛应用于排序、查找、变换等算法场景。随着C++11引入Lambda表达式,这一机制得到极大简化,开发者可以就地定义匿名函数对象,通过捕获列表灵活访问外部变量。本文聚焦函数对象与Lambda的核心机制,深入解析捕获列表、mutable关键字、性能优化等关键点,并结合STL算法展示其在排序、累积

weixin_33701564的博客 331

C++ 什么是函数,以及函数的使用

函数指针有缺点,最重要的是它无法持有自己的状态(局部状态),也无法达到组件技术中的可适配性(也就是无法再将来某些修饰徐条件加上,以达到改变其状态)。因此,STL算法的特殊版本所接受的所谓的”条件“或”策略“或”一整组操作“都是以仿函数呈现的。 所谓仿函数functor)就是使用起来像函数一样的东西。如果对某个class进行operator()重载,它就是一个仿函数类,该函数就是一个仿函数 //仿函数类 template<class T> struct Greater { //仿函数 b

WhiteShirtI的博客 3429

STL求最长上升子序列

/*有一个长度为n的数列,a1,a2,a3,a4,a5…… an,请求出这个序列中的最长上升子序列的长度,上升子序列指的是对于任意的i<j时, ai<aj的子序列。 1<=n<=100; 0<=ai<=100000;] 上面的源代码中使用了lower_bound这是STL函数,这个函数从已排好序的a中利用二分搜索指向满足ai>=k的ai的最小指针, 类似的函数还有upper_bound,这一函数

~鹏鹏在努力~ 696

C++之函数

#include&lt;iostream&gt; #include&lt;vector&gt; #include&lt;algorithm&gt; using namespace std; /* *函数操作符()-----仿函数 *把类对象函数名一样使用 *函数,就是使用一个类的使用看上去像一个函数,其实现就是类中实现一个 *operator(),这个类就有了类似函数的行为,就是一个...

fight_onlyfor_you的博客 3563

C/C++:C++函数

C++函数: 所谓的函数,就是说它不是一个真正的函数,而是一个类或者说是一个结构体。 #include void say_hello(){ std::cout << "hello world !" << std::endl; } class Hello{ public: void operator () (){ std::c

Roope的专栏 2460

【笔记】【c++】仿函数(函数)理解

operator() 重载 (),且接受两个 const int ,const int 参数,最后的 const 用于修饰 返回值。# operator() 视为一个整体,将 () 重载为一个函数,即 {} 中的操作。# 用这种方式将 () 重载为 函数调用,方便代码阅读。# 可根据传入参数个数和类型的不同, 重载多次 ()

coole_true的博客 378

STL函数函数对象functor初步理解(上)

函数或者函数对象只是翻译的问题,英文全部都是functor,还有些读物管这个叫函数符。functor作为C++ STL六大基本组件之一被广泛使用(其他五个分别是容器、演算法、迭代器、适配器、分配器),那么,functor的意义在哪里?或者说它有什么作用? 这项技术从概念上不难理解函数也好,函数对象也好,都说明了它不是真正的函数,而是一种类似函数作用的东西,在STL里,我们经常需要对

Linuxtiger之家 3694

STL函数对象实战:functor与lambda如何选?

你是否在使用STL算法时纠结过该用函数对象(functor)还是lambda表达式?作为C++标准库的核心特性,这两种函数对象机制各有优势却常被混淆。本文将通过MSVC STL实现的真实代码案例,从性能、可读性和适用场景三个维度带你彻底搞懂两者差异。读完本文你将能够: - 识别STL源码中的functor实现 - 掌握lambda与functor的转换技巧 - 学会在不同场景下选择最优方案 ##...

gitblog_00372的博客 304

探索仿函数Functor):C++中的灵活函数对象

仿函数Functor)是一种行为类似函数对象,它可以被用作函数并接受参数。在C++中,仿函数通常是重载了函数调用运算符operator()的类对象。通过重载operator(),仿函数可以像函数一样被调用,并且可以保存状态信息。按照操作数划分:假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为一元仿函数(unary functor);如果重载的operator()要求获取两个参数,就将这个类称为二元仿函数(binary functor)。

无敌岩雀的博客 3080

【C++】关于仿函数Functor理解和应用

仿函数是C++中一种强大的编程工具,它通过重载operator()使得类的实例可以像函数一样被调用。根据重载的operator()所需的参数数量,仿函数可以分为一元仿函数(Unary Functor)和二元仿函数(Binary Functor)。算术仿函数:如std::plus、std::minus、std::multiplies和std::divides,用于执行基本的算术运算。关系仿函数:如std::less、std::greater、std::equal_to等,用于比较两个元素的大小或是否相等。

richard_yuu的博客 637

2022-1-19 C++STL—— 仿函数 functor

仿函数是为了算法而服务的。 我的理解是,相较于函数,仿函数能够实现泛型参数。 如果一个 class 里面重载小括号,这样的函数创建出来的对象就叫做函数对象。 如果想要自己写出的 functorSTL 体系融为一体,需要继承 binary_function 和 unique_function 。 仿函数需要继承的模型。来是实现真正的泛型传入参数。 这一页关于适配器完全没有看懂 ...

weixin_51187533的博客 139

深入理解C++函数对象:从原理到实践

摘要:C++函数对象通过重载"()"运算符使类实例具备函数调用能力,结合了对象函数的优势。相比普通函数函数对象具有状态保存、高度定制和编译优化三大特性,能实现跨调用数据共享和灵活行为定制。STL提供了算术、比较等预定义函数对象,开发者也可根据需求自定义带状态的函数对象。与函数指针和Lambda相比,函数对象更适合需要复用、状态管理和性能优化的场景。实践建议:简单逻辑用Lambda,基础算法用STL预定义函数对象,复杂需求则自定义函数对象,将"()"运算符设为con

物换星移几度秋 843

STL中仿函数(functors)、类成员和mem_fun的使用

STL中仿函数(functors)、类成员和mem_fun的使用作者: winter 众所周知,STL使用起来非常方便,其中仿函数(functor)扮演了一个非常重要的角色。灵活运用仿函数的使用对于发挥STL强大功能非常关键。本文详细介绍了如何使用mem_fun和mem_fun1来绑定类成员函数,使之成为functor什么是仿函数?就是一个重载了"()"运算符的struct,例如:st

fisher_jiang的专栏 1404

深入理解仿函数functorfunction object)

仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。   有些功能的的代码,会在不同的成员函数中用到,想复用这些代码。                             1)公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就

Belial计算机视觉&机器视觉专栏 1840

C++ STL 学习 :更多仿函数(functor)(二)

By zieckey( All right reserved!) C++ STL 学习 :更多仿函数(functor)(二) C++标准程序库中提供了许多非常有用的预先定义好的的仿函数,了解这些将为我们的开发工作带来便利性和稳健性。 求反值: // TEMPLATE STRUCT negate templateclass _Ty>     struc

Damon_X的博客 611

⚡【C++要笑着学】(24) 模拟实现栈和队列类:Functor 仿函数 | 模拟实现 stack | 模拟实现 queue | 模拟实现 priority_queue

在上一章中,我们讲解了STL的栈和队列,本章我们来模拟实现一下它们。在讲解优先级队列的同时我们顺便把上一章提到的仿函数进行一个讲解,使用仿函数可以有效替换使用难以理解函数指针的场景。我们通过仿函数 less 和 greater 去控制优先级队列的 Compare,从而能同时适配升序和降序。............

有道无术, 术尚可求。有术无道, 止于术。 1701

C++ 函数对象函数符)

本文主要参考:文章1、文章2。 目录 1. 什么是函数对象 2. 函数对象的按值传递 3. 函数适配器 3.1 绑定器 3.2 取反器 4. 自定义容器的排序 1. 什么是函数对象 尽管函数指针被广泛用于实现函数回调,但是回调函数是来自C语言的,在C++中应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。更重要的是,C++提供了另外一个可以实现回调函数的方法,那...

爱吃柚子的好奶罐 1539

()C++中的functor与lambda

c++在函数后加const的意义是,定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。不过,上述的方式是通过硬编码实现的,换一个加的值就要重新写一个函数,十分不方便。不过,模板函数的方式是在编译时需要确定参数和类型,因此其必须时。类型,不能传参数进去,也就是像如下方式,程序运行时会报错。,是任何像函数一样的函数。欢迎访问个人网络日志🌹🌹。...

机器人视觉感知 876
上一篇: STL中伪函数、函数对象(functor)初步理解(上)
下一篇: Visual Studio 2010下生成ACE静态库方法
对象
博客等级 码龄17年 70粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值