当前位置: 首页 > news >正文

跟我学c++中级篇——c++中的Abominable Function Types

一、Abominable Function Types

Abominable Function Types,令人讨厌(憎恶)的函数类型。这个在c++的技术点中,很少有人了解。那么什么是Abominable Function Types呢?看下面的例子:

using func    = void();
using func_aft = void() const volatile &&;

在上面的两行代码中,声明了两个函数。第一行的代码大家一般都比较熟悉,一般函数都这样声明。但下面一行的代码就比较不一般了,它带了const、volatile和 &&(&)等限定符。要知道,限定符可是在成员函数中才能使用的,它隐式调用了*this,可这里偏偏它又是一个特定的函数类型而不类成员,这就有一个问题了,没有办法指定它的所属类。
换句话说,根本没办法实现了一个这样的函数,只是有一种想象罢了。举个不恰当的例子,“我要去火星”,这个想法没错,但实现不了(至少目前实现不了)。
同时,如果想声明一个此种类型的指针和引用时,会报一个“ill-formed”错误,即下面的代码:

using p = func_aft*;
using r = func_aft&;

这种函数就是一个令人讨厌的函数类型,一般来说,这种类型都具有这个特征,特定的函数带有限定符。
那它有什么用呢?

二、作用

先铺垫一下引用限定符和const。在早期的开发中,如果不想让某个变量被改变,那么可以使用如下的方法来操作:

void Test(const A &a)
{//此处无法修改A的成员值
}

在c++11中其实引进了另外一种方式,也就是&,&&,看下面的例子:

#include <iostream>class T {
public:T(int n):n_(n){}int get()&{return this->n_;}
private:int n_;
};
int main() {T t(1);std::cout << t.get() << std::endl;          // OK//std::cout << std::move(t).get() << std::endl;  // errorreturn 0;
}

同理,右值也可以使用上面的代码只是把&改成&&即可。效果也会发生变化,左值就无法使用了,只能使用右值。这时,如果想实现上面早期的方式,可以增加const,注意const一定要在&和&&之前:

#include <iostream>class T {
public:T(int n,int n1):n_(n),n1_(n1){}int get()const &{return this->n_;}int getr()const &&{return this->n1_;}
private:int n_;int n1_
};

不过此时需要注意的是,const & 修饰时,此时的值可以是左值也可以是右值,和单纯引用限定时有区别。而const &&时仍然只能是右值。
好,介绍完了背景知识,回到正题。Abominable Function Types有什么作用?
从一个例子开始说明,在c++11中引入了std::function这个对象,那么可以任意声明一个变量如下:

struct T
{void operator()() {/* ... */} // not const!
};
const std::function<void()> func(T{});
func(); // OK

在std::function其本身的operator()它是一个常函数,但T的是一个普通函数,这样,一个常量就可以调用一个非常量函数,那么它在多线程执行时,就无法达到要求的不产生况态条件。此时就可以用Abominable Function Types来处理它,即只有拿到的函数类型为const才构建需要的对象。这个在c++23中的move_only_function就是如此做的。

三、实际的应用

在看到上面的分析和说明后,就可以看看它在实际工程中到底有什么作用?先说明一点,它基本上都是在模板元编程用的,其它方向上确实非常非常少。在Traits类型时,这些约束应用到引用和指针时,就需要额外处理这种令人讨厌的函数类型。

template <typename TYPE>
constexpr bool is_function_v = false;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...)> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......)> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) volatile &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) volatile &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile &> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS...) const volatile &&> = true;template <typename RESULT, typename ...ARGS>
constexpr bool is_function_v<RESULT(ARGS......) const volatile &&> = true;

*代码摘自Alisdair Meredith, ameredith1@bloomberg.net,论文《Abominable Function Types》
上面的代码用来处理各种情况下的函数类型的判断,可以测试所有能想到的函数类型保证模板的安全。
下面再看一个可能在实际中遇到的情况:

template <typename T>
struct remove_member_pointer { using type = T; };template <typename T, typename classType>
struct remove_member_pointer<T classType::*> { using type = T; };
struct T {void test() { std::cout << "abc" << std::endl; };
};
using mfType = typename remove_member_pointer<decltype(&T::test)>::type;

看一下c++的STL中remove_pointer的可能实现:

template< class T > struct remove_pointer                    {typedef T type;};
template< class T > struct remove_pointer<T*>                {typedef T type;};
template< class T > struct remove_pointer<T* const>          {typedef T type;};
template< class T > struct remove_pointer<T* volatile>       {typedef T type;};
template< class T > struct remove_pointer<T* const volatile> {typedef T type;};

在上面的代码去除上一层的T*是非常容易理解的,而去除函数指针中的classType可能有点不好理解,classType::*可以代表其任意的成员函数,在实际的remove_member_pointer过程中,只是提供了一个typedef的函数指针,这里如果有不明白的可以看一下函数指针中如何使用typedef,这个在std::add_pointer中也有体现:“Otherwise (if T is a cv- or ref-qualified function type), provides the member typedef type which is the type T.The behavior of a program that adds specializations for std::add_pointer is undefined.”

四、总结

今天分析的这个令人讨厌的函数类型和c++11中的引用限定符,都是比较少使用的,在实际工程中估计绝大多数国内的c++程序员都无法用到。那就了解一下,省得再看国外的框架代码时遇到这样的问题,搞不清楚。
消除这种令人讨厌的函数类型有很多种,最简单的就是禁止它们。但这又无法兼容旧得版本标准,这本身就是一个非常不好体验。那么只能选择完善它,在c++23中就对其进行了处理,比如使用this(前面分析过的Deducing This)来处理一些等效的操作等。
一个知识存在,一定有其存在的意义,当确实遇到需要它的问题时,才会发现确实是有用。

相关文章:

跟我学c++中级篇——c++中的Abominable Function Types

一、Abominable Function Types Abominable Function Types,令人讨厌&#xff08;憎恶&#xff09;的函数类型。这个在c的技术点中&#xff0c;很少有人了解。那么什么是Abominable Function Types呢&#xff1f;看下面的例子&#xff1a; using func void(); using func…...

计算机毕设之基于python+django+mysql的影片数据爬取与数据分析(包含源码+文档+部署教程)

影片数据爬取与数据分析分为两个部分&#xff0c;即管理员和用户。该系统是根据用户的实际需求开发的&#xff0c;贴近生活。从管理员处获得的指定账号和密码可用于进入系统和使用相关的系统应用程序。管理员拥有最大的权限&#xff0c;其次是用户。管理员一般负责整个系统的运…...

slog正式版来了:Go日志记录新选择!

在大约一年前&#xff0c;我就写下了《slog&#xff1a;Go官方版结构化日志包[1]》一文&#xff0c;文中介绍了Go团队正在设计并计划在下一个Go版本中落地的Go官方结构化日志包&#xff1a;slog[2]。但slog并未如预期在Go 1.20版本[3]中落地&#xff0c;而是在golang.org/x/exp…...

华为静态路由配置实验(超详细讲解+详细命令行)

系列文章目录 华为数通学习&#xff08;7&#xff09; 前言 一&#xff0c;静态路由配置 二&#xff0c;网络地址配置 AR1的配置&#xff1a; AR2的配置&#xff1a; AR3的配置&#xff1a; 三&#xff0c;测试是否连通 AR1的配置: 讲解&#xff1a; AR2的配置&#…...

axios源码学习

1 判断一个对象是否普通对象 Symbol.toStringTag&#xff1a;可以修改Object.prototype.toString.call返回的后缀&#xff0c;普通对象自带该属性&#xff0c;不需要设置&#xff0c;如果设置说明该对象不是普通对象Symbol.iterator&#xff1a;拥有该属性的对象可以使用for o…...

【SpingBoot】详细介绍SpringBoot项目中前端请求到数据库再返回前端的完整数据流转,并用代码实现

在SpringBoot项目中&#xff0c;前端请求到最终返回的完整数据流转一般包括以下几个步骤&#xff1a; 前端发送HTTP请求到后端Controller。 Controller接收到请求后&#xff0c;调用相关Service处理业务逻辑。 Service调用DAO层获取数据。 DAO层访问数据库获取数据。 数据库…...

kubesphere devops使用

一、创建项目 1 创建项目 企业管理员切换到相应企业空间(租户),创建项目&#xff0c;k8s集群会创建一个相同名字的namespace。如下图所示管理员创建一个ipaas-devops项目。 2.创建镜像拉取密钥信息 进入项目如ipaas-devops&#xff0c;选择配置->保密字典->创建&#xf…...

Selenium如何用于编写自动化测试脚本?

Selenium如何用于编写自动化测试脚本&#xff1f;它提供了许多测试工具和API&#xff0c;可以与浏览器交互&#xff0c;模拟用户操作&#xff0c;检查网页的各个方面。下面是一些步骤&#xff0c;可以帮助你编写Selenium自动化测试脚本。 1、安装Selenium库和浏览器驱动程序 首…...

linux入门到精通-第二章-常用命令和工具

目录 概述命令格式帮助文档内建命令外部命令&#xff08;--help&#xff09;帮助文档查看man查看谁登陆过电脑 文件目录命令创建目录显示目录结构删除目录 文件相关命令ls命令touchcprm删除mv移动命令 文件查看命令cat 文件内容查看命令less 查看文件内容head 从文件头部查看ta…...

C语言初阶测评题:测试你的基础知识和编程技能!!

&#x1f493;博客主页&#xff1a;江池俊的博客⏩收录专栏&#xff1a;C语言刷题专栏&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅C语言进阶之路&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐ 文…...

使用HTTPS模式建立高效爬虫IP服务器详细步骤

嘿&#xff0c;各位爬虫小伙伴们&#xff01;想要自己建立一个高效的爬虫IP服务器吗&#xff1f;今天我就来分享一个简单而强大的解决方案——使用HTTPS模式建立工具&#xff01;本文将为你提供详细的操作步骤和代码示例&#xff0c;让你快速上手&#xff0c;轻松建立自己的爬虫…...

每日一题 230二叉搜索树中第K小的元素(中序遍历)

题目 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;1示例 2&#xff1a; 输入…...

文件包含漏洞及漏洞复现

文件包含漏洞 1. 文件包含概述 程序开发人员通常会把可重复使用函数或语句写到单个文件中&#xff0c;形成“封装”。在使用某个功能的时候&#xff0c;直接调用此文件&#xff0c;无需再次编写&#xff0c;提高代码重用性&#xff0c;减少代码量。这种调用文件的过程通常称为…...

Android 手游聚合SDK小知识(一)

Android 手游聚合SDK小知识(一) Android 手游聚合SDK小知识(二) 聚合分包 前言 回头想想&#xff0c;在安卓游戏SDK这个领域&#xff0c;我也呆了4年了&#xff0c;从啥都不懂的小菜鸟&#xff0c;逐渐靠自己不断学习&#xff0c;对这个行业也算有了一些理解&#xff0c;趁着…...

桂理理工大题

#include <stdio.h> #include <stdlib.h>int getMax(int n); int getMin(int n); int range(int n); static int count1; //作为全局变量控制每次的序列号int main(){int num;int i,j;do{printf("输入黑洞数&#xff1a;\n");scanf("%d",&…...

Jmeter接口测试+压力测试

接口测试 Jmeter-http接口脚本 一般分五个步骤:&#xff08;1&#xff09;添加线程组 &#xff08;2&#xff09;添加http请求 &#xff08;3&#xff09;在http请求中写入接入url、路径、请求方式和参数 &#xff08;4&#xff09;添加查看结果树 &#xff08;5&#xff09;…...

mysql‘逻辑删除‘和‘唯一索引‘冲突的解决方案

一、冲突出现原因 在user表中将name字段设置唯一索引&#xff0c;添加逻辑删除字段del_flag&#xff08;1为删除&#xff0c;0为未删除&#xff09;之后&#xff0c;将name张四的字段删除&#xff0c;再添加一个name张四的记录则会出现冲突 二、解决 1.设置唯一索引组&#x…...

MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布

一、MQTT介绍 1.1 什么是MQTT&#xff1f; MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的“轻量级”通讯协议&#xff0c;该协议构建于…...

gRPC-Gateway 快速实战

今天来分享一波 gRPC-Gateway &#xff0c; 之前咱们有分享过什么是 gRPC 及其使用方式&#xff0c;可以看看这些关于 gRPC 的历史文章&#xff1a; gRPC介绍 gRPC 客户端调用服务端需要连接池吗&#xff1f; gRPC的拦截器 gRPC的认证 分享一下 gRPC- HTTP网关 I 今天主要是分…...

〔019〕Stable Diffusion 之 单图中绘制多人分区域写提示词 篇

✨ 目录 🎈 下载区域绘制插件🎈 区域绘制使用🎈 参数讲解和基础使用🎈 Lora 自组🎈 Lora 自组的使用🎈 分区扩散🎈 分区域提示🎈 下载区域绘制插件 在绘制图片时,经常绘制的图片不仅仅是 单人图片,也可能需要绘制 多人图片那么通过正常方式绘制出来的多人图片…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...