《C++11:bind绑定器与function包装器》

C++STL中提供了bind1绑定器,通常与函数对象一起使用。

函数对象是重载了operator()函数的对象。

将二元函数对象operator()的第一个参数绑定为固定的x来构造一元函数对象。返回绑定了第一个参数的函数对象。

将二元函数对象operator()的第二个参数绑定为固定的x来构造一元函数对象。返回绑定了第二个参数的函数对象。 

我们看以下代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>using namespace std;template<typename Container>void showContainer(Container &con)
{typename Container::iterator it = con.begin();for (; it != con.end(); it++){cout << *it << " ";}cout << endl;
}int main()
{vector<int> vec;srand(time(nullptr));for (int i = 0; i < 10; i++){vec.push_back(rand() % 100 + 1);}showContainer(vec);sort(vec.begin(), vec.end()); // 默认从小到大排序showContainer(vec);// greater 二元函数对象sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象showContainer(vec);return 0;
}

如果现在要将60按顺序插入到vector,那么就要在vector中找第一个小于60的数字。可以使用泛型算法中的find_if在指定范围中查找满足特定条件的元素。需要接收的是一个一元函数对象。

template <class InputIterator, class UnaryPredicate>InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);

pred:一个一元谓词,是一个可调用对象,接收一个参数,返回布尔值,返回true表示条件满足。

此时,需要从vector中取一个对象跟60比,那么不能使用库中的greater或者less函数对象,因为它们是一个二元函数对象,接收两个值。

那我们应该怎样处理呢?就引入了绑定器bind1st、bind2nd。

bind1st和bind2nd

通过绑定器绑定一个二元函数对象,就可以得到一个一元函数对象。

bind1st:要在vector中找小于60的元素,可以将greater的第一个参数绑定为60,就将greater间接的变为了一元函数对象了,传参的时候只需要传给greater的第二个参数即可。

bind2nd:要在vector中小于60的元素,可以将less的第二个参数绑定为60,就将less间接的变为了一元函数对象了,传参的时候只需要传给less的第一个参数即可。

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<ctime>using namespace std;template<typename Container>void showContainer(Container &con)
{typename Container::iterator it = con.begin();for (; it != con.end(); it++){cout << *it << " ";}cout << endl;
}int main()
{vector<int> vec;srand(time(nullptr));for (int i = 0; i < 10; i++){vec.push_back(rand() % 100 + 1);}showContainer(vec);sort(vec.begin(), vec.end()); // 默认从小到大排序showContainer(vec);// greater 二元函数对象sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象showContainer(vec);// 将60按顺序插入到vector中 在vector中找第一个小于60的元素auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 60));if (it1 != vec.end()){vec.insert(it1, 60);}showContainer(vec);auto it2 = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 60));if (it2 != vec.end()){vec.insert(it2, 60);}showContainer(vec);return 0;
}

bind1st和bind2nd的底层实现原理

下面我们来模拟实现bind1st和bind2nd来探究以下它们的底层实现原理。

using namespace std;template<typename Container>void showContainer(Container &con)
{typename Container::iterator it = con.begin();for (; it != con.end(); it++){cout << *it << " ";}cout << endl;
}// 模拟实现find_if
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare com)
{for (; first != last; first++){if (com(*first)) // com.operator()(*first)return first;}return last;
}// 实现_mybind1st
template<typename Compare,typename T>
class _mybind1st
{
public:_mybind1st(Compare comp, T val):_comp(comp),_val(val){}// bind1st(greater<int>(), 60)bool operator()(const T& second){return _comp(_val, second); // 底层还是二元函数对象来作用的 greater}
private:Compare _comp;T _val;
};// 模拟实现bind1st
template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare com, const T& val)
{// 返回的是一元函数对象return _mybind1st<Compare,T>(com, val);
}// 实现_mybind2nd
template<typename Compare, typename T>
class _mybind2nd
{
public:_mybind2nd(Compare comp, T val):_comp(comp), _val(val){}// bind1st(greater<int>(), 60)bool operator()(const T& first){return _comp(first, _val); // 底层还是二元函数对象来作用的 greater}
private:Compare _comp;T _val;
};// 模拟实现bind2nd
template<typename Compare, typename T>
_mybind2nd<Compare, T> mybind2nd(Compare com, const T& val)
{// 返回的是一元函数对象return _mybind2nd<Compare, T>(com, val);
}int main()
{vector<int> vec;srand(time(nullptr));for (int i = 0; i < 10; i++){vec.push_back(rand() % 100 + 1);}showContainer(vec);sort(vec.begin(), vec.end()); // 默认从小到大排序showContainer(vec);// greater 二元函数对象sort(vec.begin(), vec.end(), greater<int>()); // 从大到小排序,传入函数对象showContainer(vec);// 将60按顺序插入到vector中 在vector中找第一个小于60的元素auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 60));if (it1 != vec.end()){vec.insert(it1, 60);}showContainer(vec);auto it2 = my_find_if(vec.begin(), vec.end(), mybind2nd(less<int>(), 60));if (it2 != vec.end()){vec.insert(it2, 60);}showContainer(vec);return 0;
}

bind1st或bind2nd绑定二元函数对象,可以生成一个一元函数对象,一元函数对象底层还是二元函数对象来实现的。

bind1st和bind2nd应用在在编程中如果想使用一个一元函数对象,但是库中没有,只有二元函数对象,我们就可以通过绑定器绑定二元函数对象的某一个参数,进而将它变成一个一元函数对象进而来使用。

bind1st和bind2nd有一个非常大的局限性,只能绑定二元函数对象,但是实际中,使用到的参数可能是三个或者以上,所以C++11就从Boost库中引入了更强大的bind绑定器。

C++11bind

std::bind是一个函数模板,可以看作是一个函数绑定器(适配器),它允许将一个可调用对象和其部分或全部参数进行绑定,生成一个新的可调用对象。这个新的可调用对象可以在后续的代码中被调用,并且可以省略掉已经绑定的参数。

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

简单演示一下std::bind的使用方法:

#include <iostream>
#include <functional>
using namespace std;int add(int a, int b)
{return a + b;
}
struct Adder
{int operator()(int a, int b){return a + b;}
};
class Calculator
{
public:int add(int a, int b){return a + b;}
};
int main() {// 1、绑定普通函数// 使用 std::bind 绑定 add 函数和它的参数auto addFunc1 = std::bind(add, 3, 5);// 调用绑定后的可调用对象cout << addFunc1() << endl; //就相当于调用 add(3,5)// 使用占位符预留参数位置auto addFunc2 = std::bind(add, std::placeholders::_1, std::placeholders::_2);cout << addFunc2(3, 5) << endl;// 混合使用占位符和参数auto addFunc3 = std::bind(add, std::placeholders::_1, 5);cout << addFunc3(3) << endl;// 参数调换顺序auto addFunc4 = std::bind(add, std::placeholders::_2, std::placeholders::_1);cout << addFunc4(3, 5) << endl;// 2、绑定函数对象Adder adder;auto addFunc5 = std::bind(Adder(), std::placeholders::_1, std::placeholders::_2);cout << addFunc5(2, 2) << endl;auto addFunc6 = std::bind(adder, std::placeholders::_1, std::placeholders::_2);cout << addFunc6(2, 2) << endl;//相当于调用adder(1,2)// 3、绑定lambda表达式auto addFunc7 = std::bind([](int a, int b) {return a + b; }, std::placeholders::_1, std::placeholders::_2);cout << addFunc7(3, 3) << endl;// 2、绑定成员函数Calculator calc;auto addFunc8 = std::bind(&Calculator::add, &calc, std::placeholders::_1, std::placeholders::_2);cout << addFunc8(4, 4) << endl;//相当于cal.add(1,2)return 0;
}

function

bind绑定器,函数对象,Lambda表达式都只能在一条语句中使用,那么如果想在其他地方使用,该如何使用呢?可不可以留下函数对象的类型呢?C++11就引入了function。

function是一个强大的包装器,可以存储、赋值任何可调用函数,包括普通函数、函数指针、成员函数指针、函数对象(仿函数)以及 lambda 表达式。它就像一个容器,能将不同类型的可调用对象统一封装起来,以统一的方式进行调用。

我们先来看下function的简单应用:

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
#include<ctime>using namespace std;void hello1()
{cout << "hello1 world!" << endl;
}
void hello2(string str)
{cout << str << endl;
}
int sum(int a, int b)
{return a + b;
}
class Test
{
public:void hello(string str){cout << str << endl;}
};
int main()
{// 使用函数类型实例化function// 包装普通函数function<void()> func1 = hello1;func1(); // func1.operator()() => hello1()function<void(string)> func2 = hello2;func2("hello2 world!"); // func2.operator()() => hello2()function<int(int, int)> func3 = sum;cout << func3(10, 20) << endl; // func3.operator()() => sum()// 包装成员函数Test test;function<void(Test*, string)> func4 = &Test::hello;func4(&test, "Test::hello world!");// 包装函数对象function<int(int, int)> func5 = [](int a, int b)->int {return a + b; };cout << func5(100, 200) << endl; // operator()return 0;
}

  1. 使用函数类型实例化function
  2. 通过function调用operator()函数的时候,需要根据函数类型传入相应参数

但是上面的例子,好像并没有看出function的好处,还不如直接调用函数呢,下面我们实现段代码看一下使用function的好处。


#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<algorithm>
#include<functional>
#include<ctime>using namespace std;void doShowAllBooks() { cout << "查看所有书籍" << endl; }
void doBack() { cout << "还书" << endl; }
void borrow() { cout << "借书" << endl; }
void doQueryBooks() { cout << "查询书籍信息" << endl; }
void dologinOut() { cout << "注销" << endl; }int main()
{int choice;map<int, function<void()>> actionMap;actionMap.insert({ 1, doShowAllBooks });actionMap.insert({ 2, doBack });actionMap.insert({ 3, borrow });actionMap.insert({ 4, doQueryBooks });actionMap.insert({ 5, dologinOut });for (;;){cout << "-----------------" << endl;cout << "查看所有书籍" << endl;cout << "还书" << endl;cout << "借书" << endl;cout << "查询书籍" << endl;cout << "注销" << endl;cout << "-----------------" << endl;cin >> choice;auto it = actionMap.find(choice);if (it == actionMap.end()){cout << "输入数字无效,请重新选择!" << endl;}else{it->second();}}// 不实用,这段代码无法闭合,无法做到“开-闭”原则/*switch (choice){case 1:cout << "查看书籍" << endl;break;case 2:cout << "还书" << endl;break;case 3:cout << "借书" << endl;break;case 4:cout << "查询" << endl;break;case 5:cout << "注销" << endl;break;default:break;}*/return 0;
}

function函数对象类型的实现原理


#include<iostream>
#include<string>
#include<functional>
using namespace std;void hello(string str)
{cout << str << endl;
}
int sum(int a, int b)
{return a + b;
}// 通用模板,作为偏特化的基础
template<typename Fty>
class myfunction {};/*
// 模板偏特化允许针对特定的模板参数类型或者参数组合,提供专门的实现
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:using PFUNC = R(*)(A1);myfunction(PFUNC pfunc):_pfunc(pfunc){}R operator()(A1 arg){return _pfunc(arg); // hello("hello world!")}
private:PFUNC _pfunc;
};
template<typename R, typename A1, typename A2>
class myfunction<R(A1, A2)>
{
public:using PFUNC = R(*)(A1, A2);myfunction(PFUNC pfunc):_pfunc(pfunc){}R operator()(A1 arg1, A2 arg2){return _pfunc(arg1, arg2); // hello("hello world!")}
private:PFUNC _pfunc;
};
*/// 函数参数不确定,难道要特化很多模板出来吗?不需要,C++11提供了可变参数模板
template<typename R, typename... A>
class myfunction<R(A...)>
{
public:using PFUNC = R(*)(A...);myfunction(PFUNC pfunc):_pfunc(pfunc){}R operator()(A... arg){return _pfunc(arg...); // hello("hello world!") sum(10,20)}
private:PFUNC _pfunc;
};int main()
{myfunction<void(string)> func1(hello);func1("hello world!"); // func1.operator()("hello world!")myfunction<int(int, int)> func2(sum);cout << func2(10, 20) << endl; //func2.operator()(10,20)return 0;
}

function其实就是一个函数对象(重载了operator()函数),底层封装了一个函数指针,指向了要包装的函数,通过可变参数模板,operator()()可以接收类型及任意数量的参数,将参数传递给内部,内部使用函数指针进行函数的调用。

总结:

std::function和std::bind虽然都与可调用对象相关,但它们的功能和用途各有侧重。std::function主要用于存储和调用可调用对象,实现回调和多态;std::bind主要用于参数绑定和调整参数顺序,简化函数调用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/41857.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS 防抖与节流

防抖 核心思想&#xff1a;延迟执行&#xff0c;只有在事件触发的频率降低到一定程度后才会执行&#xff0c;而且如果事件持续触发&#xff0c;之前地所有执行都会被取消。 有的操作是高频触发的&#xff0c;但是其实只需要一次触发。比如短时间内多次缩放页面resize&#xff0…

【C/C++算法】从浅到深学习--- 简单模拟算法(图文兼备 + 源码详解)

绪论&#xff1a;冲击蓝桥杯一起加油&#xff01;&#xff01; 每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本篇是一些简单的模拟算法&#xff0c;其中模拟的本质就是就是根据题目意思进行代码的…

​​解锁 JavaScript DOM:节点操作的核心方法与最佳实践

引言 在当今动态化的 Web 世界中&#xff0c;用户早已不满足于静态的网页展示&#xff0c;而是期待流畅的交互体验——点击按钮弹出菜单、滚动页面加载数据、实时搜索过滤内容……这些功能的背后&#xff0c;都离不开 ​JavaScript DOM&#xff08;文档对象模型&#xff09;操…

Android 12系统源码_系统启动(二)Zygote进程

前言 Zygote&#xff08;意为“受精卵”&#xff09;是 Android 系统中的一个核心进程&#xff0c;负责 孵化&#xff08;fork&#xff09;应用进程&#xff0c;以优化应用启动速度和内存占用。它是 Android 系统启动后第一个由 init 进程启动的 Java 进程&#xff0c;后续所有…

精华贴分享|从不同的交易理论来理解头肩形态,殊途同归

本文来源于量化小论坛策略分享会板块精华帖&#xff0c;作者为孙小迪&#xff0c;发布于2025年2月17日。 以下为精华帖正文&#xff1a; 01 前言 学习了一段时间交易后&#xff0c;我发现在几百年的历史中&#xff0c;不同门派的交易理论对同一种市场特征的称呼不一样&#x…

C++智能指针万字详细讲解(包含智能指针的模拟实现)

在笔试&#xff0c;面试中智能指针经常出现&#xff0c;如果你对智能指针的作用&#xff0c;原理&#xff0c;用法不了解&#xff0c;那么可以看看这篇博客讲解&#xff0c;此外本博客还简单模拟实现了各种指针&#xff0c;在本篇的最后还应对面试题对智能指针的知识点进行了拓…

学习threejs,使用多面体(IcosahedronGeometry、TetrahedronGeometry、OctahedronGeometry等)

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PolyhedronGeometry …

DeepSeek详解:探索下一代语言模型

文章目录 前言一、什么是DeepSeek二、DeepSeek核心技术2.1 Transformer架构2.1.1 自注意力机制 (Self-Attention Mechanism)(a) 核心思想(b) 计算过程(c) 代码实现 2.1.2 多头注意力 (Multi-Head Attention)(a) 核心思想(b) 工作原理(c) 数学描述(d) 代码实现 2.1.3 位置编码 (…

【目标检测】【深度学习】【Pytorch版本】YOLOV1模型算法详解

【目标检测】【深度学习】【Pytorch版本】YOLOV1模型算法详解 文章目录 【目标检测】【深度学习】【Pytorch版本】YOLOV1模型算法详解前言YOLOV1的模型结构YOLOV1模型的基本执行流程YOLOV1模型的网络参数YOLOV1模型的训练方式 YOLOV1的核心思想前向传播阶段网格单元(grid cell)…

网络运维学习笔记(DeepSeek优化版) 022 HCIP-Datacom路由概念、BFD协议详解与OSPF第一课

文章目录 路由概念、BFD协议详解与OSPF第一课一、路由协议优先级与选路原则1.1 路由协议优先级对照表1.2 路由选路核心原则 二、BFD&#xff08;Bidirectional Forwarding Detection&#xff0c;双向转发检测&#xff09;的配置与应用2.1 双向心跳探测&#xff08;双端配置&…

单应性矩阵(homography)

利用单应性矩阵计算内外参矩阵 利用单应性矩阵解决问题 问题描述&#xff1a;

Scavenge算法的优缺点问题

Scavenge 的缺点是只能使用堆内存中的一半&#xff0c;这是由划分空间和复制机制所决定的。但 Scavenge 由于只复制存活的对象&#xff0c;并且对于生命周期短的场景&#xff0c;存活对象只占少部分&#xff0c;所以它在时间效率上有优异的表现。 由于 Scavenge 是典型的牺牲空…

丝杆支撑座间隙调整不当会带来哪些影响?

丝杆支撑座是一种用于支撑滚珠丝杆的零件&#xff0c;通常用于机床、数控机床、自动化生产线等高精度机械设备中。支撑座间隙调整不当会对机械设备的运行产生多方面的影响&#xff0c;接下来一起了解一下&#xff1a; 1、降低加工精度&#xff1a;在机械加工设备中&#xff0c;…

Unity:EasyRoad3D插件学习 二期

前言&#xff1a; 书接上回。 一、场景视图状态&#xff1a; 创建好道路以后&#xff0c;切换到第一个选项&#xff0c;场景视图状态&#xff0c;查看道路信息&#xff0c;Main Settings修改道路名称、类型&#xff0c;宽度&#xff0c;是否闭环。 RoadWidth改为15&#xff…

内网渗透-DLL和C语言加载木马

免杀进阶技术 1、DLL的定义与使用 DLL:Dynamic Link library,动态链接库&#xff0c;是一个无法自己运行&#xff0c;需要额外的命令或程序来对其接口进行调用&#xff08;类方法、函数&#xff09;。 (1)在DevCpp中创建一个DLL项目 (2)在dllmain.c中定义源代码函数接口 #i…

一洽让常见问题的快速咨询,触手可及

在客户服务场景中&#xff0c;重复性常见问题的处理效率直接影响用户体验与客服成本。针对重复性常见问题&#xff0c;如何以直观的方式呈现给用户&#xff0c;使其能够快速、精准地提出咨询&#xff0c;已成为提升客户满意度的关键因素。 一、传统客服模式的效率枷锁 用户咨…

WEB攻防-Java安全SPEL表达式SSTI模版注入XXEJDBCMyBatis注入

目录 靶场搭建 JavaSec ​编辑​编辑 Hello-Java-Sec(可看到代码对比) SQL注入-JDBC(Java语言连接数据库) 1、采用Statement方法拼接SQL语句 2.PrepareStatement会对SQL语句进行预编译&#xff0c;但如果直接采取拼接的方式构造SQL&#xff0c;此时进行预编译也无用。 3、…

树莓集团南京园区启航:数字经济新地标!

深耕数字产业&#xff0c;构筑生态闭环 树莓集团在数字产业领域拥有超过十年的深厚积累&#xff0c;专注于构建“数字产业”的融合生态链。其核心优势在于有效整合政府、产业、企业及高校资源&#xff0c;形成一个协同创新、价值共生的产业生态闭环系统。 赋能转型&#xff0c…

Redis之bimap/hyperloglog/GEO

bimap/hyperloglog/GEO的真实需求 这些需求的痛点&#xff1a;亿级数据的收集清洗统计展现。一句话&#xff1a;存的进取得快多维度 真正有价值的是统计。 统计的类型 亿级系统中常见的四种统计 聚合统计 统计多个集合元素的聚合结果&#xff0c;就是交差并等集合统计。 排…

nara wpe去混响学习笔记

文章目录 1.WPE方法去混响的基本流程1.1.基本流程 2.离线迭代方法3.在线求法3.1.回顾卡尔曼方法3.2.在线去混响递推滤波器G方法 nara wpe git地址 博客中demo代码下载 参考论文 NARA - WPE: A Python Package for Weighted Prediction Error Dereverberation in Numpy and Ten…