[C++]std::function用法

从函数指针到std::function
函数指针的作用大家应该十分熟悉,它使得我们可以把函数当成参数传递。在C语言中,这种方法几乎非常完美,可以实现基本上所有的传递函数的操作。但是在C++中,由于仿函数的出现,C语言中的函数指针变得不通用了。(仿函数就是重载了operator()的类。)

void func()
{
    std::cout << "普通函数";
}
class T
{
public:
    void operator()()
    {
        std::cout << "仿函数";
    }
};

想要绑定func函数,非常简单,只需要使用以下代码即可:

void (*p1)(void) = func;
p1();

然而,同样的方法并不能绑定仿函数!因为在C++中,仿函数可能读取成员变量,所以被视为与普通函数不同的类型。要想绑定仿函数,我们必须这样:

void (T:: * p2)(void) = &T::operator();
(T().*p2)();

这样一来,对于仿函数的操作就麻烦了许多,所以,我们必须寻求一种通用的方法。这个方法就是std::function。

使用std::function
std::function是C++标准库中的一个模板类,用来保存一个函数、仿函数或lambda表达式。std::function重载了operator(),使得调用函数就是普通的方式。使用前需要包含头文件<functional>。使用格式是:

std::function<返回值类型(参数类型)> 对象名(函数、仿函数或lambda表达式);
例如上面的例子就可以写成:

std::function<void(void)> f1(func),f2(T());
f1();
f2();

值得注意的是,std::function的模板参数不能省略参数类型外面的括号,即使没有参数也不能。例如,上例就不能写成

std::function<void> f1(func);//会报错
1
另外,模板参数也不能指定noexcept。例如std::function<void(void)noexcept>也是不行的。但这并不意味着不能绑定noexcept函数,如果想绑定noexcept函数直接用std::function<void(void)>即可。

std::function与lambda
值得注意的是,lambda表达式的类型并不是std::function,而是一种编译器自动生成的类,与std::function类似,lambda表达式也重载了operator(),但它们的类型和std::function并不一样。为什么std::function能存储lambda表达式呢?因为lambda表达式能够隐式转换为函数指针,这样就可以再转为std::function了。

std::function的缺点
std::function虽然很好用,但是也有一点缺陷,使其不能完全代替函数指针。这个缺陷就是,因为它可以绑定仿函数,所以它不能作为编译期常量。如下面的例子:

#include <iostream>
#include<functional>
void func()
{
    std::cout << "test";
}
consteval std::function<void(void)> GetFunc1()
{
    return func;
}
using FuncType = void(*)(void);
consteval FuncType GetFunc2()
{
    return func;
}
int main()
{
    return 0;
}

函数GetFunc1会报错,因为std::function不能作为编译期常量。

正因为如此,std::function不能作为模板的非类型参数的类型,但是函数指针可以。

std::function的可读性
如果在使用函数指针和std::function都行的情况下,应该优先选用哪个呢?我个人的建议是优先使用std::function,因为他比起函数指针来可读性大大增加(C++函数指针的语法特别变态,特别是复杂了的话就非常难懂)。比如下面这个例子:

using T = void(*(*)(void))(void);
1
你认为T是什么类型?相信大多数人肯定一脸懵逼。其实T是一个函数指针,指向的函数A返回值又是一个函数指针,指向函数B,A无参数,B的返回值是void,B无参数。
再拿这个例子,如果把它转成typedef,应该把类型名称放哪里?这又是个很麻烦的问题,其实,转成typedef是这样的:

typedef void(*(*T)(void))(void);
试问这样的代码是给人看的?对于这种问题,有两种解决方法,一是及时使用using来提高可读性,如:

using T1 = void(*)(void);//函数指针,无参无返回值,相信这个大家还能看懂
using T = T1(*)(void);//函数指针,无参,返回值类型是T1

C++的函数指针变态就变态在星号和参数列表等位置特别反人类。具体来说,上面这个例子这样可以编译通过,但是如果直接把T1带入到T中求出Td的完整形式,那对不起,你想得太简单了,根本编译通不过!这就是函数指针的变态之处。而这样把T1看成一个整体,就提高了可读性。
还有一种办法,就是使用std::function。使用std::function的代码:

using T = std::function<std::function<void(void)>(void)>;
这样是不是特别清晰明了?这个函数返回值是std::function<void(void)>,无参数,一眼就能看出来,也符合人类的习惯。所以,在函数指针和std::function都可以使用的情况下,推荐使用后者。

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

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

相关文章

python装饰器@runWithTry的解析

出发点&#xff1a; 在函数的开始位置有时会看到runWithTry的字样&#xff0c;这样的好处是什么呢&#xff1f; 解析&#xff1a; Python 装饰器&#xff0c;用于包装一个函数&#xff0c;可能为了提供异常处理、日志记录、性能监测等额外功能。在 Python 中&#xff0c;装饰…

如何选择使用哪种剪枝方法?

确定使用哪种剪枝方法通常需要考虑多个因素,包括你的模型类型、任务类型、数据集特性以及训练资源。以下是一些指导原则: 模型类型: 决策树: 对于决策树,可以考虑使用预剪枝或后剪枝。预剪枝通常在构建树的过程中根据节点属性进行判断,而后剪枝则在树已构建完成后根据一些…

用c# 自己封装的Modbus工具类库源码

前言 Modbus通讯协议在工控行业的应用是很多的&#xff0c;并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用&#xff0c;对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其所以然。所以就有了自己封装的Modbus工具类库…

Mysql 常用数据类型

数值型(整数)的基本使用 如何定义一个无符号的整数 数值型(bit)的使用 数值型(小数)的基本使用 字符串的基本使用 字符串使用细节 日期类型的基本使用

前缀和算法

一、简析前缀和 有一系列元素 A [ a 0 , a 1 , . . . , a n , . . . ] A[a_0,~a_1,~...,~a_n,~...] A[a0​, a1​, ..., an​, ...]&#xff0c;前缀和 p r e _ s u m [ n ] A [ 0 ] A [ 1 ] ⋅ ⋅ ⋅ A [ n ] pre\_sum[n]A[0]A[1]A[n] pre_sum[n]A[0]A[1]⋅⋅⋅A[n]。 …

Orange3数据预处理(列选择组件)数据角色及类型描述

在Orange3的文件组件中&#xff0c;datetime、categorical、numeric以及text代表不同种类的数据类型&#xff0c;具体如下&#xff1a; datetime&#xff1a;代表日期和时间类型的数据。通常用于时间序列分析、生存分析和其他需要考虑时间因素的机器学习任务中。例如&#xff0…

图像读取裁剪与人脸识别

图像读取 Image read ⇒ \Rightarrow ⇒ torchvision.datasets from torchvision import datasets dataset datasets.ImageFolder(data_dir, transformtransforms.Resize((512, 512)))Return value illustration dataset[0][0]是PIL.Image objects&#xff0c;这利用IPyth…

小红书关键词爬虫

标题 1 统计要收集的关键词,制作一个文件夹2 爬取每一页的内容3 爬取标题和内容4 如果内容可以被查看,爬取评论内容5 将结果进行汇总,并且每个帖子保存为一个json文件,具体内容6 总结1 统计要收集的关键词,制作一个文件夹 例如,我要收集旅游相关的,就收集: 旅游、旅行…

模型训练基本结构

project_name/ │ ├── data/ │ ├── raw/ # 存放原始数据 │ ├── processed/ # 存放预处理后的数据 │ └── splits/ # 存放数据集划分&#xff08;训练集、验证集、测试集等&#xff09; │ ├── noteboo…

关系型数据库事务的四性ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)

关系型数据库事务的四性ACID:原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09; 事务的四性通常指的是数据库事务的ACID属性&#xff0c;包括原子性&…

Python从入门到精通指南【第101篇—入门到精通】【文末送书-24】

文章目录 Python从入门到精通指南第一步&#xff1a;入门基础1.1 安装Python1.2 Hello World1.3 变量和数据类型1.4 控制流程 第二步&#xff1a;深入学习2.1 函数和模块2.2 列表、元组和字典2.3 文件操作 第三步&#xff1a;高级主题3.1 面向对象编程3.2 异常处理3.3 正则表达…

蓝桥杯刷题--python-12

3768. 字符串删减 - AcWing题库 nint(input()) sinput() res0 i0 while(i<n): if s[i]x: ji1 while(j<n and s[j]x): j1 resmax(j-i-2,0) ij else: i1 print(res) 3777. 砖块 - AcWing题库 # https://www.a…

大型电商日志离线分析系统(一)

一、项目需求分析 某大型网站日志离线分析系统 1.1 概述 该部分的主要目标就是描述本次项目最终七个分析模块的页面展示。 1.2 工作流 在我们的demo展示中&#xff0c;我们使用jqueryecharts的方式调用程序后台提供的rest api接口&#xff0c;获取json数据&#xff0c;然后…

《极简C++学习专栏》之结束语

朋友们&#xff0c;经过这么长的时间&#xff0c;《极简C学习专栏》的文章创作就要结束了&#xff0c;感谢你们一路陪伴&#xff01; 也希望你们能支持我接下来的其他专栏的创作&#xff01; 专栏的初衷 《极简C学习》专栏的初衷源自于我个人的学习笔记&#xff0c;记录下自己…

网络安全与信创产业发展:构建数字时代的护城河

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

数字人的未来:数字人对话系统 Linly-Talker + 克隆语音 GPT-SoVITS

&#x1f680;数字人的未来&#xff1a;数字人对话系统 Linly-Talker 克隆语音 GPT-SoVITS https://github.com/Kedreamix/Linly-Talker 2023.12 更新 &#x1f4c6; 用户可以上传任意图片进行对话 2024.01 更新 &#x1f4c6; 令人兴奋的消息&#xff01;我现在已经将强…

Vue | (六)使用Vue脚手架(下)| 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue 中的自定义事件&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;全局事件总线&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;消息订阅与发布&#x1f407;使用方法…

一文读懂 Python 全局变量和局部变量

文章目录 版本前言全局变量和局部变量全局变量局部变量全局变量与局部变量的关系 总结个人简介 版本 Python 3.9 前言 在 Python 编程中&#xff0c;全局变量和局部变量是非常重要的概念之一。全局变量是在整个程序范围内可见和可用的变量&#xff0c;而局部变量则是在特定作…

数据解锁:.ma1x0勒索病毒攻击下的数据安全保障

导言&#xff1a; 在数字化时代&#xff0c;我们享受着便捷的科技带来的便利&#xff0c;但与此同时&#xff0c;网络犯罪也在不断演变。其中&#xff0c;.ma1x0勒索病毒是一种令人恐惧的威胁&#xff0c;它可以轻松地将您的数据文件变成数字人质&#xff0c;威胁着您的个人和…

嵌入式C语言(一)

最初我是golang出生&#xff0c;当时做项目的时候java、c、js、python都折腾过&#xff0c;但是关于c语言的接触&#xff0c;基本上都停留在大一的那个暑假。 后面一个项目需要写驱动&#xff0c;再到后门需要做DFX&#xff0c;再到我打开内核的源码&#xff0c;我一脸懵逼&am…