当前位置: 首页 > 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)来处理一些等效的操作等。
一个知识存在,一定有其存在的意义,当确实遇到需要它的问题时,才会发现确实是有用。

http://www.lryc.cn/news/153478.html

相关文章:

  • 计算机毕设之基于python+django+mysql的影片数据爬取与数据分析(包含源码+文档+部署教程)
  • slog正式版来了:Go日志记录新选择!
  • 华为静态路由配置实验(超详细讲解+详细命令行)
  • axios源码学习
  • 【SpingBoot】详细介绍SpringBoot项目中前端请求到数据库再返回前端的完整数据流转,并用代码实现
  • kubesphere devops使用
  • Selenium如何用于编写自动化测试脚本?
  • linux入门到精通-第二章-常用命令和工具
  • C语言初阶测评题:测试你的基础知识和编程技能!!
  • 使用HTTPS模式建立高效爬虫IP服务器详细步骤
  • 每日一题 230二叉搜索树中第K小的元素(中序遍历)
  • 文件包含漏洞及漏洞复现
  • Android 手游聚合SDK小知识(一)
  • 桂理理工大题
  • Jmeter接口测试+压力测试
  • mysql‘逻辑删除‘和‘唯一索引‘冲突的解决方案
  • MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
  • gRPC-Gateway 快速实战
  • 〔019〕Stable Diffusion 之 单图中绘制多人分区域写提示词 篇
  • Scala入门,idea关联Scala
  • 3DCAT携手华为,打造XR虚拟仿真实训实时云渲染解决方案
  • Spring Security注销后未正确保存空的SecurityContext漏洞CVE-2023-20862
  • 11、监测数据采集物联网应用开发步骤(8.2)
  • 【PWN · ret2text | RISC-V异构】[2023 羊城杯]login
  • 【HBZ分享】TCP可靠性传输如何保证的?以及传输优化之Nagle算法
  • 智能电销机器人,主要体现的价值是什么
  • Win7系统电脑开机总出现硬盘自检的简单解决方法
  • 计网第四章(网络层)(五)
  • ios 手机浏览器,点击输入框页面会放大
  • 全局异常处理