跟我学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,令人讨厌(憎恶)的函数类型。这个在c的技术点中,很少有人了解。那么什么是Abominable Function Types呢?看下面的例子: using func void(); using func…...

计算机毕设之基于python+django+mysql的影片数据爬取与数据分析(包含源码+文档+部署教程)
影片数据爬取与数据分析分为两个部分,即管理员和用户。该系统是根据用户的实际需求开发的,贴近生活。从管理员处获得的指定账号和密码可用于进入系统和使用相关的系统应用程序。管理员拥有最大的权限,其次是用户。管理员一般负责整个系统的运…...

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

华为静态路由配置实验(超详细讲解+详细命令行)
系列文章目录 华为数通学习(7) 前言 一,静态路由配置 二,网络地址配置 AR1的配置: AR2的配置: AR3的配置: 三,测试是否连通 AR1的配置: 讲解: AR2的配置&#…...
axios源码学习
1 判断一个对象是否普通对象 Symbol.toStringTag:可以修改Object.prototype.toString.call返回的后缀,普通对象自带该属性,不需要设置,如果设置说明该对象不是普通对象Symbol.iterator:拥有该属性的对象可以使用for o…...
【SpingBoot】详细介绍SpringBoot项目中前端请求到数据库再返回前端的完整数据流转,并用代码实现
在SpringBoot项目中,前端请求到最终返回的完整数据流转一般包括以下几个步骤: 前端发送HTTP请求到后端Controller。 Controller接收到请求后,调用相关Service处理业务逻辑。 Service调用DAO层获取数据。 DAO层访问数据库获取数据。 数据库…...

kubesphere devops使用
一、创建项目 1 创建项目 企业管理员切换到相应企业空间(租户),创建项目,k8s集群会创建一个相同名字的namespace。如下图所示管理员创建一个ipaas-devops项目。 2.创建镜像拉取密钥信息 进入项目如ipaas-devops,选择配置->保密字典->创建…...
Selenium如何用于编写自动化测试脚本?
Selenium如何用于编写自动化测试脚本?它提供了许多测试工具和API,可以与浏览器交互,模拟用户操作,检查网页的各个方面。下面是一些步骤,可以帮助你编写Selenium自动化测试脚本。 1、安装Selenium库和浏览器驱动程序 首…...

linux入门到精通-第二章-常用命令和工具
目录 概述命令格式帮助文档内建命令外部命令(--help)帮助文档查看man查看谁登陆过电脑 文件目录命令创建目录显示目录结构删除目录 文件相关命令ls命令touchcprm删除mv移动命令 文件查看命令cat 文件内容查看命令less 查看文件内容head 从文件头部查看ta…...

C语言初阶测评题:测试你的基础知识和编程技能!!
💓博客主页:江池俊的博客⏩收录专栏:C语言刷题专栏👉专栏推荐:✅C语言初阶之路 ✅C语言进阶之路💻代码仓库:江池俊的代码仓库🎉欢迎大家点赞👍评论📝收藏⭐ 文…...

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

每日一题 230二叉搜索树中第K小的元素(中序遍历)
题目 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。 示例 1: 输入:root [3,1,4,null,2], k 1 输出:1示例 2: 输入…...

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

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

桂理理工大题
#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("输入黑洞数:\n");scanf("%d",&…...
Jmeter接口测试+压力测试
接口测试 Jmeter-http接口脚本 一般分五个步骤:(1)添加线程组 (2)添加http请求 (3)在http请求中写入接入url、路径、请求方式和参数 (4)添加查看结果树 (5)…...

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

MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
一、MQTT介绍 1.1 什么是MQTT? MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于…...

gRPC-Gateway 快速实战
今天来分享一波 gRPC-Gateway , 之前咱们有分享过什么是 gRPC 及其使用方式,可以看看这些关于 gRPC 的历史文章: gRPC介绍 gRPC 客户端调用服务端需要连接池吗? gRPC的拦截器 gRPC的认证 分享一下 gRPC- HTTP网关 I 今天主要是分…...
〔019〕Stable Diffusion 之 单图中绘制多人分区域写提示词 篇
✨ 目录 🎈 下载区域绘制插件🎈 区域绘制使用🎈 参数讲解和基础使用🎈 Lora 自组🎈 Lora 自组的使用🎈 分区扩散🎈 分区域提示🎈 下载区域绘制插件 在绘制图片时,经常绘制的图片不仅仅是 单人图片,也可能需要绘制 多人图片那么通过正常方式绘制出来的多人图片…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...