C++STL---<functional>

news2025/4/6 19:17:28

C++标准库中的 <functional> 库是一个强大的工具集,它提供了用于处理函数对象、函数绑定、函数包装等功能的设施,极大地增强了代码的灵活性和可复用性。

1. 函数对象(Functors)

函数对象,也被称作仿函数,是重载了 () 运算符的类的实例。其使用方式与普通函数调用类似,但函数对象可以携带状态,这使得它比普通函数更加灵活。

#include <iostream>

// 定义一个函数对象类
class Adder {
public:
    int operator()(int a, int b) const {
        return a + b;
    }
};

int main() {
    Adder adder;
    int result = adder(3, 4);
    std::cout << "3 + 4 = " << result << std::endl;
    return 0;
}
  • 原理:在 Adder 类中,重载了 () 运算符,该运算符接受两个 int 类型的参数并返回它们的和。Adder 类的对象 adder 就可以像函数一样被调用,当调用 adder(3, 4) 时,实际上是调用了 Adder 类的 operator() 方法。
  • 优点:函数对象可以包含成员变量,这些成员变量可以在对象创建时进行初始化,并且在多次调用 operator() 时保持状态,这是普通函数无法做到的。

2. std::function

std::function 是一个通用的多态函数包装器,它可以存储、复制和调用任何可调用对象,包括普通函数、函数指针、成员函数指针、函数对象和 lambda 表达式等。

#include <iostream>
#include <functional>

// 普通函数
int add(int a, int b) {
    return a + b;
}

int main() {
    // 使用 std::function 包装普通函数
    std::function<int(int, int)> func = add;
    int result = func(3, 4);
    std::cout << "3 + 4 = " << result << std::endl;

    // 包装 lambda 表达式
    std::function<int(int, int)> lambdaFunc = [](int a, int b) { return a * b; };
    result = lambdaFunc(3, 4);
    std::cout << "3 * 4 = " << result << std::endl;
    return 0;
}
  • 模板参数std::function<int(int, int)> 中的模板参数指定了可调用对象的签名,即接受两个 int 类型的参数并返回一个 int 类型的结果。
  • 包装过程:当将普通函数 add 或 lambda 表达式赋值给 std::function 对象时,std::function 会内部存储该可调用对象,并提供统一的调用接口。当调用 func(3, 4)lambdaFunc(3, 4) 时,std::function 会根据存储的可调用对象进行相应的调用。
  • 用途std::function 常用于实现回调机制,例如在事件处理、多线程编程等场景中,通过 std::function 可以方便地传递和调用不同类型的可调用对象。

3. std::bind

std::bind 用于创建一个新的可调用对象,它将一个可调用对象和其部分或全部参数绑定在一起,允许进行部分参数绑定和参数重排。

#include <iostream>
#include <functional>

// 普通函数
int add(int a, int b) {
    return a + b;
}

int main() {
    // 使用 std::bind 绑定 add 函数的第一个参数为 3
    auto add3 = std::bind(add, 3, std::placeholders::_1);
    int result = add3(4);
    std::cout << "3 + 4 = " << result << std::endl;

    // 参数重排
    auto sub = [](int a, int b) { return a - b; };
    auto reversedSub = std::bind(sub, std::placeholders::_2, std::placeholders::_1);
    result = reversedSub(3, 4);
    std::cout << "4 - 3 = " << result << std::endl;
    return 0;
}
  • 部分参数绑定:在 std::bind(add, 3, std::placeholders::_1) 中,add 是要绑定的可调用对象,3 是第一个参数,std::placeholders::_1 是一个占位符,表示新可调用对象的第一个参数。add3 是一个新的可调用对象,当调用 add3(4) 时,实际上是调用 add(3, 4)
  • 参数重排:在 std::bind(sub, std::placeholders::_2, std::placeholders::_1) 中,通过占位符的顺序交换了 sub 函数的参数顺序。当调用 reversedSub(3, 4) 时,实际上是调用 sub(4, 3)
  • 用途std::bind 可以用于简化函数调用,特别是在需要固定部分参数的情况下,或者在需要对参数顺序进行调整的情况下。

4. std::mem_fn

std::mem_fn 用于创建一个可调用对象,该对象可以调用类的成员函数。它可以处理成员函数指针,使得调用成员函数更加方便。

#include <iostream>
#include <functional>

class MyClass {
public:
    void print() const {
        std::cout << "Hello, World!" << std::endl;
    }
    int add(int a, int b) const {
        return a + b;
    }
};

int main() {
    MyClass obj;
    // 使用 std::mem_fn 包装成员函数
    auto printFunc = std::mem_fn(&MyClass::print);
    printFunc(obj);

    auto addFunc = std::mem_fn(&MyClass::add);
    int result = addFunc(obj, 3, 4);
    std::cout << "3 + 4 = " << result << std::endl;
    return 0;
}
  • 成员函数指针&MyClass::print&MyClass::add 是成员函数指针,它们指向 MyClass 类的 printadd 成员函数。
  • 包装过程std::mem_fn 接受成员函数指针作为参数,返回一个可调用对象。当调用 printFunc(obj) 时,std::mem_fn 会将 obj 作为调用 print 成员函数的对象,从而实现成员函数的调用。同样,addFunc(obj, 3, 4) 会调用 objadd 成员函数,并传递 34 作为参数。
  • 用途std::mem_fn 使得成员函数的调用可以像普通函数调用一样方便,特别是在需要将成员函数作为参数传递给其他函数时非常有用。

5. 预定义的函数对象

<functional> 库提供了一系列预定义的函数对象,用于常见的操作,如算术运算、比较运算等。

算术运算
#include <iostream>
#include <functional>

int main() {
    std::plus<int> add;
    int result = add(3, 4);
    std::cout << "3 + 4 = " << result << std::endl;

    std::minus<int> sub;
    result = sub(4, 3);
    std::cout << "4 - 3 = " << result << std::endl;
    return 0;
}
  • std::plusstd::plus<int> 是一个预定义的函数对象,它重载了 () 运算符,用于执行两个 int 类型数的加法运算。当调用 add(3, 4) 时,实际上是调用了 std::plus<int>operator() 方法,返回 3 + 4 的结果。
  • std::minusstd::minus<int> 用于执行两个 int 类型数的减法运算,其原理与 std::plus 类似。
比较运算
#include <iostream>
#include <functional>

int main() {
    std::greater<int> greater;
    bool isGreater = greater(4, 3);
    std::cout << "4 > 3: " << (isGreater ? "true" : "false") << std::endl;

    std::less<int> less;
    isGreater = less(4, 3);
    std::cout << "4 < 3: " << (isGreater ? "true" : "false") << std::endl;
    return 0;
}
  • std::greaterstd::greater<int> 用于比较两个 int 类型数的大小,返回 true 表示第一个参数大于第二个参数。当调用 greater(4, 3) 时,返回 true
  • std::lessstd::less<int> 用于比较两个 int 类型数的大小,返回 true 表示第一个参数小于第二个参数。当调用 less(4, 3) 时,返回 false

6. std::refstd::cref

std::refstd::cref 用于创建引用包装器,当需要将引用传递给 std::bind 或其他需要按值传递参数的地方时非常有用。

#include <iostream>
#include <functional>

void increment(int& num) {
    ++num;
}

int main() {
    int num = 3;
    auto func = std::bind(increment, std::ref(num));
    func();
    std::cout << "num = " << num << std::endl;
    return 0;
}
  • std::refstd::ref(num) 创建了一个对 num 的引用包装器。在 std::bind(increment, std::ref(num)) 中,由于 increment 函数接受一个引用参数,使用 std::ref 可以确保传递给 increment 的是 num 的引用,而不是 num 的副本。当调用 func() 时,increment 函数会修改 num 的值。
  • std::crefstd::crefstd::ref 类似,但它创建的是一个常量引用包装器,用于传递常量引用。

7. std::hash

std::hash 是一个模板类,用于生成对象的哈希值,常用于实现哈希表等数据结构。

#include <iostream>
#include <functional>

int main() {
    std::hash<int> hasher;
    int num = 3;
    std::size_t hashValue = hasher(num);
    std::cout << "Hash value of " << num << " is " << hashValue << std::endl;
    return 0;
}
  • 哈希函数std::hash<int> 是一个针对 int 类型的哈希函数模板类,它重载了 () 运算符,接受一个 int 类型的参数并返回一个 std::size_t 类型的哈希值。当调用 hasher(num) 时,会根据 num 的值生成一个对应的哈希值。
  • 用途:在实现哈希表、哈希集合等数据结构时,std::hash 可以方便地为元素生成哈希值,从而实现快速的查找和插入操作。

8. std::not_fn (C++17 起)

std::not_fn 用于创建一个新的可调用对象,该对象返回原可调用对象结果的逻辑非。

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>

bool isEven(int num) {
    return num % 2 == 0;
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    auto isOdd = std::not_fn(isEven);
    auto oddCount = std::count_if(numbers.begin(), numbers.end(), isOdd);
    std::cout << "Number of odd numbers: " << oddCount << std::endl;
    return 0;
}
  • 逻辑非操作std::not_fn(isEven) 创建了一个新的可调用对象 isOdd,当调用 isOdd 时,它会调用 isEven 并返回其结果的逻辑非。在 std::count_if 中使用 isOdd 可以统计 numbers 向量中奇数的个数。
  • 用途std::not_fn 可以方便地反转一个布尔函数的逻辑,避免手动编写反转逻辑的代码。

总结

<functional> 库提供了丰富的工具来处理函数对象和函数调用,通过这些工具可以提高代码的灵活性和可维护性。合理使用这些工具可以让代码更加简洁和高效,特别是在实现回调机制、函数绑定、成员函数调用等场景中发挥重要作用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java详细笔记总结持续完善

一.Java开发环境的搭建 1. 单位换算 1TB 1024GB 1GB 1024MB 1MB 1024KB 1KB 1024Byte (字节) 1Byte 8 bit(位) 注意&#xff1a;一个字节占8位 2. DOS命令 DOS : Disk Operation System 磁盘操作系统 即用于操作本地磁盘的系统 命令操作符号盘符切换命令盘符名:查看当前文…

wsl2的centos7安装jdk17、maven

JDK安装 查询系统中的jdk rpm -qa | grep java按照查询的结果&#xff0c;删除对应版本 yum -y remove java-1.7.0-openjdk*检查是否删除 java -version 下载JDK17 JDK17&#xff0c;下载之后存到wsl目录下&#xff08;看你自己&#xff09;然后一键安装 sudo rpm -ivh jd…

乐鑫ESP-Mesh-Lite方案,启明云端乐鑫代理商,创新组网拓展智能应用边界

在当今智能化浪潮的背景下&#xff0c;智能家居、智能农业、能源管理等领域对设备组网的需求日益增长。然而&#xff0c;传统的Wi-Fi组网方式常常受限于设备数量、路由器位置以及网络覆盖范围等因素&#xff0c;难以满足复杂场景下的多样化需求。 一方面&#xff0c;需要支持更…

ISIS【路由协议讲解】-通俗易懂!

IS-IS的背景 IS-IS最初是国际标准化组织ISO为它的无连接网络协议CLNP&#xff08;ConnectionLess Network Protocol&#xff09;设计的一种动态路由协议。随着TCP/IP协议的流行&#xff0c;为了提供对IP路由的支持&#xff0c;IETF在相关标准中对IS-IS进行了扩充和修改&#xf…

Vitis HLS 学习笔记--块级控制(IDE 2024.1 + 执行模式 + 默认接口实现)

目录 1. 简介 2. 默认接口实现 2.1 执行模式 2.2 接口范式 2.2.1 存储器 2.2.2 串流 2.3.3 寄存器 2.3 Vitis Kernel Flow 2.3.1 默认的协议 2.3.2 vadd 代码 2.3.3 查看报告 2.4 Vivado IP Flow 2.4.1 默认的协议 2.4.2 vadd 代码 2.4.3 查看报告 3. 测试与波…

红宝书第二十一讲:详解JavaScript的模块化(CommonJS与ES Modules)

红宝书第二十一讲&#xff1a;详解JavaScript的模块化&#xff08;CommonJS与ES Modules&#xff09; 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、模块化的意义&#xff1a;分而治之 模块化解决代码依赖混…

github 页面超时解决方法

github 页面超时解决方法 每次好不容易找到github项目代码之后&#xff0c;满心欢喜打开却是个无法访问&#xff0c;心顿时又凉了半截&#xff0c;现在有方法可以访问github啦 快来学习 打开浏览器插件&#xff08;Edge浏览器&#xff09; 搜索iLink插件 并安装 打开插件 填…

前端 vue 项目上线前操作

目录 一、打包分析 二、CDN加速 三、项目部署 1. 打包部署 2. nginx 解决 history 刷新 404 问题 3. nginx配置代理解决生产环境跨域问题 一、打包分析 项目编写完成后&#xff0c;就需要部署到服务器上供他人访问。但是在此之前&#xff0c;我们可以先预览项目的体积大…

vue: easy-cron扩展-更友好地显示表达式

我们一个批处理调度系统里要用到cron表达式&#xff0c;于是就在网上找到一个现成的组件easy-cron&#xff0c;采用后发现&#xff0c;它的配置界面还是很直观的&#xff0c;但显示时直接显示cron表达式&#xff0c;这对业务人员很不友好&#xff0c;所以&#xff0c;我们就扩展…

移动零+复写零+快乐数+盛最多水的容器+有效三角形的个数

前言 2025.3.31&#xff0c;今天开始每日五道算法题&#xff0c;今天的算法题如标题&#xff01; 双指针算法 在做今天的算法题之前&#xff0c;先来介绍一下今天会用到的算法&#xff01; 双指针算法分为了两种常见的形式&#xff1a;对撞指针和快慢指针&#xff01; 对撞…

Linux中常用的文件管理命令

一、文件和目录的建立 文件 touch命令 单一文件的创建 当按下回车后我们就可以在桌面获得一个名字叫file的文件 [rootlocalhost Desktop]# touch file 同步文件访问时间和文件修改时间 由上两图可知touch file这个命令还可以把文件访问时间和文件修改时间变成touch file命…

Root Cause Analysis in Microservice Using Neural Granger Causal Discovery

Root Cause Analysis in Microservice Using Neural Granger Causal Discovery 出处:AAAI 24 摘要 近年来,微服务因其可扩展性、可维护性和灵活性而在 IT 运营中得到广泛采用。然而,由于微服务中的复杂关系,当面临系统故障时,站点可靠性工程师 (SRE) 很难查明根本原…

学习笔记—数据结构—二叉树(链式)

目录 二叉树&#xff08;链式&#xff09; 概念 结构 初始化 遍历 前序遍历 中序遍历 后序遍历 层序遍历 结点个数 叶子结点个数 第k层结点个数 深度/高度 查找值为x的结点 销毁 判断是否为完整二叉树 总结 头文件Tree.h Tree.c 测试文件test.c 补充文件Qu…

深入理解指针5

sizeof和strlen的对比 sizeof的功能 **sizeof是**** 操作符****&#xff0c;用来**** 计算****变量或类型或数组所占**** 内存空间大小****&#xff0c;**** 单位是字节&#xff0c;****他不管内存里是什么数据** int main() {printf("%zd\n", sizeof(char));p…

一文详解QT环境搭建:Windows使用CLion配置QT开发环境

在当今的软件开发领域&#xff0c;跨平台应用的需求日益增长&#xff0c;Qt作为一款流行的C图形用户界面库&#xff0c;因其强大的功能和易用性而备受开发者青睐。与此同时&#xff0c;CLion作为一款专为C/C打造的强大IDE&#xff0c;提供了丰富的特性和高效的编码体验。本文将…

NE 综合实验3:基于 IP 配置、链路聚合、VLAN 管理、路由协议及安全认证的企业网络互联与外网访问技术实现(H3C)

综合实验3 实验拓扑 设备名称接口IP地址R1Ser_1/0与Ser_2/0做捆绑MP202.100.1.1/24G0/0202.100.2.1/24R2Ser_1/0与Ser_2/0做捆绑MP202.100.1.2/24G0/0172.16.2.1/24G0/1172.16.1.1/24G0/2172.16.5.1/24R3G5/0202.100.2.2/24G0/0172.16.2.2/24G0/1172.16.3.1/24G0/2172.16.7.1/…

多段圆弧拟合离散点实现切线连续

使用多段圆弧来拟合一个由离散点组成的曲线,并且保证切线连续。也就是说&#xff0c;生成的每一段圆弧之间在连接点处必须有一阶导数连续&#xff0c;也就是切线方向相同。 点集分割 确保每个段的终点是下一段的起点&#xff0c;相邻段共享连接点&#xff0c;避免连接点位于数…

【蓝桥杯】第十四届C++B组省赛

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;蓝桥杯 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 试题A&#xff1a;日期统计试题B&#xff1a;01串的熵试题C&#xff1a;冶炼金属试题D&#xff1a;飞机降落试题E&#xff1a;接…

企业级海外网络专线行业应用案例及服务商推荐

在全球化业务快速发展的今天&#xff0c;传统网络技术已难以满足企业需求。越来越多企业开始选择新型海外专线解决方案&#xff0c;其中基于SD-WAN技术的企业级海外网络专线备受关注。这类服务不仅能保障跨国数据传输&#xff0c;还能根据业务需求灵活调整网络配置。接下来我们…

阿里云服务器安装docker以及mysql数据库

(1) 官方下载路径 官方下载地址: Index of linux/static/stable/x86_64/阿里云镜像地址: https://mirrors.aliyun.com/docker-ce/下载最新的 Docker 二进制文件&#xff1a;wget https://download.docker.com/linux/static/stable/x86_64/docker-20.10.23.tgz登录到阿里云服务…