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);
}

总结

我们需要有两个认识:

  1. std::invoke可以实现对函数对象的调用,达到与直接调用函数相同的效果
  2. 如果要实现类似回调系统、事件系统类似的功能,需要集合模板来实现

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

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

相关文章

使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b或DeepSeek-r1:1.5b 模型(完成度80%)

原文&#xff1a;&#x1f6a3;‍♂️ 使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b 模型 &#x1f6a3; — PaddleNLP 文档 使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b 模型 &#x1f6a3; PaddleNLP 在支持 AVX 指令的 CPU 上对 llama 系列模型进行了…

Kotlin高效实现 Android ViewPager2 顶部导航:动态配置与性能优化指南

高效实现&#xff1a;强调代码的性能优化。Android ViewPager2&#xff1a;明确技术栈。顶部导航&#xff1a;核心功能点。动态配置与性能优化指南&#xff1a;突出动态配置的灵活性和性能优化的重点。 在 Android 开发中&#xff0c;使用 ViewPager2 实现高效的顶部导航&…

六种最新优化算法(TOC、MSO、AE、DOA、GOA、OX)求解多个无人机协同路径规划(可以自定义无人机数量及起始点),MATLAB代码

一、算法简介 &#xff08;一&#xff09;阿尔法进化&#xff08;Alpha Evolution&#xff0c;AE&#xff09;算法 阿尔法进化&#xff08;Alpha Evolution&#xff0c;AE&#xff09;算法是2024年提出的一种新型进化算法&#xff0c;其核心在于通过自适应基向量和随机步长的…

上传本地项目到GitHub

一、在GitHub上创建仓库 1.点击右上角头像–>点击Your repositories 2.点击New 3.创建仓库 网址复制一下&#xff0c;在后面git上传时会用到 二、打开Git Bash 1.cd 进入项目所在路径 2.输入git init 在当前项目的目录中生成本地的git管理&#xff08;当前目录下出现.…

14.使用各种读写包操作 Excel 文件:辅助模块

一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas&#xff0c;直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题&#xff0c;只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings &#xff0c;因为…

ubuntu ollama+dify实践

安装ollama 官网的指令太慢了&#xff0c;使用以下指令加速&#xff1a; export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download" curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/dow…

spring boot+mybaits多条件模糊查询和分页查询

我们首先写一下多条件的模糊查询&#xff0c;首先在controller里面写一个接口&#xff0c;进行传参&#xff0c;我们这里要注意&#xff0c;之前写修改和增加的时候用的注解都是RequestBody,也就是说&#xff01;前端传过来一个json&#xff0c;数组也行&#xff0c;然后我们后…

HarmonyOS NEXT - 电商App实例四(登录界面)

登录界面是用户进入App的第一步&#xff0c;因此需要简洁明了&#xff0c;同时保持品牌风格的一致性。如&#xff1a;顶部区域为品牌LOGO展示&#xff0c;增加品牌识别度&#xff1b;中间区域为登录表单&#xff0c;包含输入框和按钮&#xff1b;底部区域为其他登录方式、注册入…

探索ima.copilot:个人知识库搭建的AI新利器

在信息爆炸的时代&#xff0c;知识的积累与管理成为了个人发展的关键。面对海量的科研文献、工作资料和各类信息&#xff0c;如何高效地构建属于自己的知识体系&#xff0c;是许多人面临的挑战。ima.copilot这款AI工具的出现&#xff0c;为解决这一难题提供了新的思路。它凭借强…

图解多头注意力机制:维度变化一镜到底

目录 一、多头注意力机制概述二、代码实现1. pyTorch 实现2. tensorFlow实现 三、维度变化全流程详解1. 参数设定2. 维度变化流程图3. 关键步骤维度变化 四、关键实现细节解析1. 多头拆分与合并2. 注意力分数计算3. 掩码处理技巧 五、完整运行示例六、总结与常见问题1. 核心优势…

Interview preparation.md

Vue 1.1 响应式系统 Vue 3 使用 Proxy 代替 Vue 2 中的 Object.defineProperty 来实现响应式系统。Proxy 可以监听对象的所有操作&#xff0c;包括属性的添加和删除&#xff0c;从而解决了 Vue 2 的一些局限性。 Vue 2&#xff1a;使用 Vue.set 添加响应式属性 new Vue({el…

2.8滑动窗口专题:最小覆盖子串

1. 题目链接 LeetCode 76. 最小覆盖子串 2. 题目描述 给定字符串 s 和 t&#xff0c;要求找到 s 中最小的窗口&#xff0c;使得该窗口包含 t 的所有字符&#xff08;包括出现次数&#xff09;。若不存在&#xff0c;返回空字符串。 示例&#xff1a; 输入&#xff1a;s &quo…

【数据分析大屏】基于Django+Vue汽车销售数据分析可视化大屏(完整系统源码+数据库+开发笔记+详细部署教程+虚拟机分布式启动教程)✅

目录 一、项目背景 二、项目创新点 三、项目功能 四、开发技术介绍 五、项目功能展示 六、权威视频链接 一、项目背景 汽车行业数字化转型加速&#xff0c;销售数据多维分析需求激增。本项目针对传统报表系统交互性弱、实时性差等痛点&#xff0c;基于DjangoVue架构构建…

cyberstrikelab lab2

lab2 重生之我是渗透测试工程师&#xff0c;被公司派遣去测试某网络的安全性。你的目标是成功获取所有服务器的权限&#xff0c;以评估网络安全状况。 先扫一下 ​ ​ 192.168.10.10 ​ ​ 骑士cms 先找后台路径 http://192.168.10.10:808/index.php?madmin&cind…

在 Ubuntu 服务器上使用宝塔面板搭建博客

&#x1f4cc; 介绍 在本教程中&#xff0c;我们将介绍如何在 Ubuntu 服务器 上安装 宝塔面板&#xff0c;并使用 Nginx PHP MySQL 搭建一个博客&#xff08;如 WordPress&#xff09;。 主要步骤包括&#xff1a; 安装宝塔面板配置 Nginx PHP MySQL绑定域名与 SSL 证书…

PTA7-13 统计工龄

题目描述 给定公司 n 名员工的工龄&#xff0c;要求按工龄增序输出每个工龄段有多少员工。 输入格式: 输入首先给出正整数 n&#xff08;≤105&#xff09;&#xff0c;即员工总人数&#xff1b;随后给出 n 个整数&#xff0c;即每个员工的工龄&#xff0c;范围在 [0, 50]。…

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Servlet 3.0 新特性:异步处理与注解配置

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、Servle…

电子电气架构 --- 汽车电子硬件架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 人生是一场骗局,最大的任务根本不是什么买车买房,也不是及时行乐,这就是欲望,不是理想,是把自己对生命的希望寄托在外物上,正确的做法应该是内…

使用 Homebrew 安装 OpenJDK 并配置环境变量

在 macOS 上使用 Homebrew 安装 OpenJDK 是一种简单而高效的方式。本文将使用 Homebrew 安装 OpenJDK&#xff0c;并设置环境变量以便 Java 能够正确运行。 1. 安装 Homebrew 首先&#xff0c;确保你的 macOS 系统已经安装了 Homebrew。如果没有安装&#xff0c;可以通过以下…

Java集合简单理解

Java 的集合框架&#xff08;Java Collections Framework, JCF&#xff09;是 Java 中用于存储和操作数据结构的核心库&#xff0c;提供了丰富的接口和实现类&#xff0c;用于处理不同类型的集合数据。以下是详细的介绍&#xff1a; 一、集合框架的体系结构 Java 集合主要分为…