当前位置: 首页 > 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 自组的使用🎈 分区扩散🎈 分区域提示🎈 下载区域绘制插件 在绘制图片时,经常绘制的图片不仅仅是 单人图片,也可能需要绘制 多人图片那么通过正常方式绘制出来的多人图片…...

官宣!网络安全法正式实施,人才缺口 327 万,这 5 类人直接站上风口,年薪百万不是梦

【必看收藏】网络安全人才抢夺战打响&#xff01;新法实施后5类专业薪资翻倍&#xff0c;附学习路线 新《网络安全法》实施引爆网络安全人才市场&#xff0c;全球缺口480万&#xff0c;中国缺口327万以上。网络空间安全、信息安全、保密技术、网络安全科学与技术、信息对抗技术…...

别再手动刷新了!Qt5/Qt6下用信号槽优雅处理串口热插拔(避坑QTimer的误用)

Qt串口热插拔检测&#xff1a;从定时轮询到事件驱动的架构升级 在工业控制、医疗设备和物联网终端开发中&#xff0c;串口通信的稳定性直接关系到系统可靠性。传统QTimer轮询方案虽然实现简单&#xff0c;但在实际项目中常遇到两个典型问题&#xff1a;一是频繁的端口扫描造成C…...

STM32H743以太网实战:基于CubeMX 6.8.0与LAN8720的LWIP移植避坑指南

1. 环境准备与CubeMX基础配置 折腾了一周终于把STM32H743的以太网调通&#xff0c;发现网上大多数教程都存在配置遗漏。这里分享我的完整配置流程&#xff0c;从CubeMX安装到最终Ping通&#xff0c;每个步骤都经过实测验证。 首先确保安装STM32CubeMX 6.8.0和对应的HAL库。我遇…...

三维姿态表达:从欧拉角、旋转矩阵到四元数的工程实践

1. 三维姿态表达的基础概念 在三维空间中描述物体的姿态&#xff08;orientation&#xff09;是许多工程领域的核心需求&#xff0c;无论是卫星姿态控制、机器人运动规划&#xff0c;还是游戏开发中的角色动画&#xff0c;都需要精确的姿态表达方式。姿态描述的本质是回答一个问…...

2026女性入局IT:这10张证书最吃香

一、引言&#xff1a;数字化转型背景下的IT职业性别结构变迁根据Gartner 2025年度全球数字化劳动力报告显示&#xff0c;女性在IT领域的参与度正从传统的“软件开发与测试”向“数据驱动决策”、“产品与项目管理”以及“用户体验设计”迁移。这一结构性变化主要归因于企业对数…...

B站成分检测器:3分钟快速安装指南,智能识别评论区用户真实身份

B站成分检测器&#xff1a;3分钟快速安装指南&#xff0c;智能识别评论区用户真实身份 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分&#xff0c;支持动态和关注识别以及手动输入 UID 识别 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-comme…...

终极Windows热键冲突解决方案:Hotkey Detective一键定位占用程序

终极Windows热键冲突解决方案&#xff1a;Hotkey Detective一键定位占用程序 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective …...

从句实战指南:从三大从句到地道英文写作

1. 从句的本质&#xff1a;让句子"活"起来的秘密武器 第一次接触英语从句时&#xff0c;我盯着课本上那句"That the earth is round is true"发呆了十分钟。主谓宾在哪&#xff1f;为什么that后面跟着完整句子&#xff1f;这种困惑持续到我发现从句就像乐高…...

STFT音高迁移:C++实现音频变调不变速的核心原理与工程实践

1. 项目概述&#xff1a;音频处理的“时间魔法师”如果你玩过音乐制作或者做过音频分析&#xff0c;肯定遇到过这样的场景&#xff1a;一段人声录音的音调有点低&#xff0c;你想把它调高一点&#xff0c;但又不想改变它说话的速度和节奏感。或者反过来&#xff0c;一段背景音乐…...

Jetson Nano到手第一步:保姆级系统烧录与基础环境配置(避坑指南)

Jetson Nano开箱实战&#xff1a;从零构建AI开发环境的完整指南 刚拆封的Jetson Nano开发板躺在桌面上&#xff0c;这块仅有信用卡大小的设备却蕴含着强大的边缘计算能力。对于初次接触嵌入式AI开发的工程师而言&#xff0c;如何正确完成系统初始化往往成为第一个技术门槛。本文…...