std::invoke详解
基础介绍
c++17版本引入了std::invoke特性,这是一个通用的调用包装器,可以统一调用:
- 普通函数
- 成员函数
- 函数对象
- Lambda表达式
- 指向成员的指针
它的主要作用是提供一个统一的方式来调用各种可调用对象。
std::invoke依赖的头文件:#include <functional>
基本用法
下面将详细介绍基本用法,即对上节中提到的对象(普通函数、成员函数、函数对象、Lambda表达式等)的调用。
#include <functional>
#include <iostream>
using namespace std;//普通函数
void basic_function(int x)
{cout <<" 普通函数:"<<x<<endl;
}//具有返回值的普通函数
int add(int a, int b)
{return a + b;
}//成员函数
class MyClass{public:void member_function(int x){cout <<"成员函数:"<<x<<endl;}int value = 33;
}//函数对象
class Functor
{public:void operator()(int x){cout <<"仿函数对象"<<x<<endl;}
}//示例函数
void basic_usage()
{//调用普通函数std::invoke(basic_function, 5);//调用具有返回值的普通函数int value = std::invoke(add, 4, 5);//调用成员函数MyClass obj;std::invoke(&MyClass::member_function, obj, 5);//调用仿函数对象Functor funtor;std::invoke(funtor, 5);//调用lambda表达式std::invoke([](int x){cout <<"lambda表达式:"<<x<<endl;
}, 5);//访问成员变量,这个成员变量必须时public的std::invoke(&Myclass::value, obj);
}
特性总结
通过上面示例可以得到以下结论:
- std::invoke表示函数调用:只要调用std::invoke,且执行了这个语句,那么就相当于调用了传入的函数对象
- std::invoke的含义传入一个函数对象及这个函数对象的参数,然后通过std::invoke完成这个函数的调用
思考:为什么引入std::invoke?
统一的调用语法
函数对象有多种,比如普通函数,成员函数、仿函数对象,lambda表达式等不同的形式,不同的函数对象的调用方法都不相同,请看下面的例子:
#include <functional>
#include <iostream>class Example {
public:void method(int x) {std::cout << "Method called: " << x << "\n";}int value = 42;
};void normal_function(int x) {std::cout << "Function called: " << x << "\n";
}void unified_call_syntax() {Example obj;// 不使用 std::invoke 时的不同调用语法normal_function(1); // 普通函数调用obj.method(2); // 成员函数调用int val = obj.value; // 成员变量访问// 使用 std::invoke 的统一语法std::invoke(normal_function, 1); // 普通函数std::invoke(&Example::method, obj, 2); // 成员函数std::invoke(&Example::value, obj); // 成员变量
}
从上面的例子可以看到,如果不使用std::invoke,那么不同的函数对象的对象方法和形式各不相同;但是引入std::invoke后,可以很明显的看到针对不同的函数对象实现了相同的调用形式。
泛型编程的支持
前面的例子是针对不同的函数对象不同调用,但是提到泛型编程,就会涉及不同的函数对象,不同的参数数量和类型。那如何设计一个函数可以实现不同的函数对象类型,不同参数数量和参数类型的调用呢?首先肯定是需要依靠模板实现的。请看下面的例子:
#include <functional>
#include <iostream>
#include <type_traits>//函数模板
template<typename F, typename... Args>
decltype(auto) modern_call(F&& f, Args&&... args)
{return std::invoke(std::forward<F>(f),std::forward<Args>(args));
}//普通函数
void normal_function(int x)
{std::cout << "Function called: " << x << "\n";
}
//示范类
class Calculator {
public:int add(int a, int b) { return a + b; }double factor = 1.5;
};void example() {Calculator calc;// 可以统一处理各种可调用对象modern_call(normal_function, 1); // 普通函数modern_call(&Calculator::add, calc, 2, 3); // 成员函数modern_call(&Calculator::factor, calc); // 成员变量modern_call([](int x) { return x * 2; }, 5); // lambda表达式
}
通过上面的例子可以看到,通过modern_call的封装,实现了不同类型的函数对象的统一调用。可以这样说,若要实现对不同函数对象的统一调用的支持,必须要依靠模板的方式实现对std::invoke的封装。那这种泛型编程的应用场景有哪些呢?
- 回调系统
- 事件系统
- 命令模式
具体请看下面的例子:
#include <functional>
#include <iostream>
#include <vector>
#include <string>// 1. 事件系统
class EventSystem {
public:template<typename F, typename... Args>void trigger(F&& handler, Args&&... args) {std::invoke(std::forward<F>(handler),std::forward<Args>(args)...);}
};// 2. 命令模式
class Command {std::function<void()> action;
public:template<typename F, typename... Args>Command(F&& f, Args&&... args) {action = [=]() {std::invoke(f, args...);};}void execute() { action(); }
};// 3. 回调系统
class CallbackSystem {
public:template<typename Callback, typename... Args>void registerCallback(Callback&& cb, Args&&... args) {callbacks.emplace_back([=]() {std::invoke(cb, args...);});}void executeAll() {for (auto& callback : callbacks) {callback();}}private:std::vector<std::function<void()>> callbacks;
};
通过上面的例子可以清楚的看到各种场景下的使用方法,但是相同点都是在函数内部都是通过定义函数模板(泛型编程)实现的。
支持智能指针和引用包装器
#include <functional>
#include <memory>
#include <iostream>class Service {
public:int process(int x) { return x * 2; }
};void smart_pointer_example() {// 智能指针支持auto ptr = std::make_shared<Service>();auto unique = std::make_unique<Service>();// std::invoke 可以直接使用智能指针int result1 = std::invoke(&Service::process, ptr, 10);int result2 = std::invoke(&Service::process, unique, 20);// 引用包装器支持Service service;auto ref = std::ref(service);int result3 = std::invoke(&Service::process, ref, 30);
}
总结
我们需要有两个认识:
- std::invoke可以实现对函数对象的调用,达到与直接调用函数相同的效果
- 如果要实现类似回调系统、事件系统类似的功能,需要集合模板来实现
相关文章:
std::invoke详解
基础介绍 c17版本引入了std::invoke特性,这是一个通用的调用包装器,可以统一调用: 普通函数成员函数函数对象Lambda表达式指向成员的指针 它的主要作用是提供一个统一的方式来调用各种可调用对象。 std::invoke依赖的头文件:#…...
第一个vue项目
项目目录 启动vue项目 npm run serve 1.vue.config.js文件 (CLI通过vue-cli-serve启动项目,解析配置配置文件vue-condig-js) // vue.config.js //引入path板块,这是Node.js的一个内置模块,用于处理文件路径,这里引用…...
基于CNN的多种类蝴蝶图像分类
基于CNN的多种类蝴蝶图像分类🦋 基于卷积神经网络对64992786张图像,75种不同类别的蝴蝶进行可视化分析、模型训练及分类展示 导入库 import pandas as pd import os import matplotlib.pyplot as plt import seaborn as sns import numpy as np from …...
Unity插件-适用于画面传输的FMETP STREAM使用方法(三)基础使用
目录 一、插件介绍 二、组件介绍 三、Game View Streaming 1、使用 FM Network UDP 的基本设置 Server Scene Client Scene 2、使用使用 FM WebSocket 的基本设置 四、Audio Streaming 五、Microphone Streaming 一、插件介绍 Unity插件-适用于画面传输的…...
微信小程序wx.request接口报错(errno: 600001, errMsg: “request:fail -2:net::ERR_FAILED“)
来看看报错 报错如下: 请求发送部分,代码如下: uni.request({url: self.serverUrl "/getRealName",method: GET,data: {"code": self.info.code,},header: {"Authorization": uni.getStorageSync(tokenHead) uni.getStorageSync(token)}}…...
使用Docker快速搭建OpenAI兼容的Embeddings与Rerank双API服务
使用Docker快速搭建OpenAI兼容的Embeddings与Rerank双API服务 前言环境准备硬件要求软件依赖双服务部署指南1. Embeddings API部署启动容器参数说明2. Rerank API部署启动容器服务验证与测试通用验证方法1. Embeddings API测试请求示例响应特征2. Rerank API测试请求示例响应解…...
基于Python+MySQL编写的(WinForm)图书管理系统
一、项目需求分析 1.1 项目介绍 项目背景 图书馆管理系统是一些单位不可缺少的部分,书籍是人类不可缺少的精神食粮,尤其对于学校来说,尤其重要。所以图书馆管理系统应该能够为用户提供充足的信息和快捷的查询手段。但一直以来人们使用传统人工的方式管…...
[贪心算法] 摆动序列
1.解析 这里我们的贪心体现在,这里我们只需要找到每一个拐点位置的数字即可, 证明: 当我们在A点时,我们下一步的选择有四种 A到D这个线段内的数字(不包括D)选择D点D到F的点F之后的点 对于A到D来说…...
WPF未来展望:紧跟技术发展趋势,探索新的可能性
WPF未来展望:紧跟技术发展趋势,探索新的可能性 一、前言二、WPF 与.NET 技术的融合发展2.1 拥抱.NET Core2.2 利用.NET 5 及后续版本的新特性 三、WPF 在新兴技术领域的应用拓展3.1 与云计算的结合3.2 融入物联网生态 四、WPF 在用户体验和设计方面的创新…...
低空经济腾飞:无人机送货、空中通勤,未来已来
近年来,低空经济逐渐成为社会关注的焦点。从无人机送货到“空中的士”,再到飞行培训的火热进行,低空经济正迎来前所未有的发展机遇。随着技术进步和政策支持,这一曾经看似遥远的未来场景,正逐步变为现实。 低空经济如何…...
http proxy的原理是什么
Http代理的原理 代理服务器会自动提取请求数据包中的HTTP请求数据发送给服务端,并将服务端的HTTP响应数据转发给发送请求的客户端,HTTP代理服务器使用的端口通常是8080。 对于Web客户端来说,代理扮演的服务器角色,接收请求&…...
Redis--补充类型
目录 一、引言 二、补充类型 1.streams 2.geospatial 3.hyperloglog 4.bitmap 5.bitfields 三、总结 一、引言 在简单学习了redis中的5个数据类型(string,list,hash,set,zset)之后,本篇文…...
关于修改 Ollama 及其模型默认路径、迁移已安装的 Ollama 程序和模型以及重启 Ollama 的操作指南
以下是关于修改 Ollama 及其模型默认路径、迁移已安装的 Ollama 程序和模型以及重启 Ollama 的操作指南,以问答格式呈现,并将涉及命令操作的部分使用代码块按执行顺序和步骤形式展示: Q1:如何修改 Ollama 及其模型的默认路径&…...
QT编译器mingw与msvc区别及环境配置
一.QT编译器mingw与msvc主要区别 二.QT开发环境配置 1. MinGW 配置 安装步骤: 通过 Qt 官方安装器 安装时勾选 MinGW 组件(如 Qt 6.7.0 MinGW 64-bit)。 确保系统环境变量包含 MinGW 的 bin 目录(如 C:\Qt\Tools\mingw1120_64…...
【css酷炫效果】纯CSS实现进度条加载动画
【css酷炫效果】纯CSS实现进度条加载动画 缘创作背景html结构css样式完整代码基础版进阶版 效果图 通过CSS渐变与背景位移动画,无需JavaScript即可创建流体动态进度条。 想直接拿走的老板,链接放在这里:https://download.csdn.net/download/u…...
Feedback-Guided Autonomous Driving
Feedback-Guided Autonomous Driving idea 问题设定:基于 CARLA 的目标驱动导航任务,通过知识蒸馏,利用特权智能体的丰富监督信息训练学生传感器运动策略函数 基于 LLM 的端到端驱动模型:采用 LLaVA 架构并添加航点预测头&#…...
图解AUTOSAR_CP_WatchdogDriver
AUTOSAR WatchdogDriver模块详解 AUTOSAR MCAL层看门狗驱动模块详细解析 目录 1. 模块概述2. 架构位置 2.1. 组件架构 3. 主要功能4. API接口5. 配置参数 5.1. 配置模型 6. 错误代码7. 状态管理 7.1. 状态机 8. 处理流程 8.1. 活动流程 9. 操作序列 9.1. 典型操作序列 10. 硬件…...
大数据学习(65)- Hue详解
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
Maven 的核心包
由于前端项目不是核心,阅读 nexus-public 源代码似乎绕远路了。nexus-oss 社区版主要就是集成 maven 的上传包、认证、包解析、包存储这几个核心功能,前端实现重新可以使用新的现代前端工具来提高生产力。故重新疏理一下 maven 的核心机制,即…...
C语言学习笔记(第三部份)
说明:由于所有内容放在一个md文件中会非常卡顿,本文件将接续C_1.md文件的第三部分 整型存储和大小端 引例: int main(void) {// printf("%d\n", SnAdda(2, 5));// PrintDaffodilNum(10000);// PrintRhombus(3);int i 0;int arr[…...
C语言经典代码题
1.输入一个4位数:输出这个输的个位 十位 百位 千位 #include <stdio.h> int main(int argc, char const *argv[]) {int a;printf("输入一个4位数:");scanf("%d",&a);printf("个位:%d\n"…...
深入理解蒸馏、Function Call、React、Prompt 与 Agent
AI基础概念与实操 一、什么是蒸馏二、如何理解Function Call、React、Prompt与Agent(一)Function Call与Agent(二)Agent中的React概念(三)Prompt与Agent的关联 实操演练function callprompt 一、什么是蒸馏…...
CVPR2025自动驾驶端到端前沿论文汇总
自动驾驶 文章目录 自动驾驶前言自动驾驶的轨迹预测论文端到端自动驾驶论文 前言 汇总CVPR2025自动驾驶前沿论文 自动驾驶的轨迹预测论文 Leveraging SD Map to Augment HD Map-based Trajectory PredictionModeSeq: Taming Sparse Multimodal Motion Prediction with Seque…...
Qt6.8实现麦克风音频输入音频采集保存wav文件
一.本文目的 实现在Qt中接收麦克风数据并保存为WAV文件,使用QAudioInput来录音,并使用QFile来保存数据到WAV文件。 开发环境:QT6.8 本文用极简代码实现,核心代码只需不到100行。 二.代码实现...
记录一个SQL自动执行的html页面
在实际工作场景中,需要运用到大量SQL语句更新业务逻辑,对程序员本身,写好的sql语句执行没有多大问题(图1),但是对于普通用户来说还是有操作难度的。因此我们需要构建一个HTML页面(图2࿰…...
分布式唯一ID
微服务 分布式唯一主键ID生成方案_微服务主键生成-CSDN博客 uid-generator-spring-boot-starter 教程-CSDN博客 https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md GitCode - 全球开发者的开源社区,开源代码托管平台...
在图像/视频中裁剪出人脸区域
1. 在图像中裁剪人脸区域 import face_alignment import skimage.io import numpy from argparse import ArgumentParser from skimage import img_as_ubyte from skimage.transform import resize from tqdm import tqdm import os import numpy as np import warnings warni…...
LuaJIT 学习(5)—— string.buffer 库
文章目录 Using the String Buffer LibraryBuffer ObjectsBuffer Method Overview Buffer Creation and Managementlocal buf buffer.new([size [,options]]) local buf buffer.new([options])buf buf:reset()buf buf:free() Buffer Writersbuf buf:put([str|num|obj] [,……...
qt介绍图表 charts 一
qt chartsj基于Q的Graphics View框架,其核心组件是QChartView和QChart.QChartView是一个显示图表的独立部件,基类为QGraphicsView.QChar类管理图表的序列,图例和轴示意图。 绘制一个cos和sin曲线图,效果如下 实现代码 #include…...
Transformer:GPT背后的造脑工程全解析(含手搓过程)
Transformer:GPT背后的"造脑工程"全解析(含手搓过程) Transformer 是人工智能领域的革命性架构,通过自注意力机制让模型像人类一样"全局理解"上下文关系。它摒弃传统循环结构,采用并行计算实现高…...
