news 2026/3/10 10:54:24

跟我学C++中级篇——std::is_invocable的分析应

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟我学C++中级篇——std::is_invocable的分析应

一、std::is_invocable说明

编程语言的发展到现在,安全性的问题已经成为了重中之重的问题。而对C++语言来说,本身就存在着非常多的细节的安全问题。在经常应用的场景中,如果调用一个函数(包括回调函数),传递的参数(类型、数量)有问题的话,极有可能产生问题。如果单纯是显式的调用还好理解,如果是在模板编程中动态调用函数时,有可能会到运行时才会发现问题。而此时的结果可能就是崩溃了。所以,在调用前进行验证(类似合规性检查),则可以防范不少的风险。
在前面的学习中,大家了解可以用SFINAE技术来进行函数的参数检查,但SFINAE的复杂的调试难度让开发者有些难以接受。所以在C++17标准中,提供了元编程接口std::is_invocable及其系列接口。使用标准的接口优势非常明显,既让开发者的编程复杂度降低,但更重要的是让代码的移植性显著的提高。

二、C++中的定义

C++17中的std::is_invocable是通过验证可调用函数对象对应的函数参数是否合规来确定函数对象的调用的安全性。对std::is_invocable系列的定义在<type_traits>头文件中,具体的声明如下:

//1、判断Fn对象(函数、函数指针、Lambda表达式等)函数调用参数是否格式正确template<class Fn,class...ArgTypes>structis_invocable;//2、判断Fn函数对象调用参数是否格式正确,并且返回值可以隐式转换为指定类型Rtemplate<class R,class Fn,class...ArgTypes>structis_invocable_r;//3、同上1且不抛出异常template<class Fn,class...ArgTypes>structis_nothrow_invocable;//4、同上2且不抛出异常template<class R,class Fn,class...ArgTypes>structis_nothrow_invocable_r;//辅助函数template<class Fn,class...ArgTypes>inlineconstexpr bool is_invocable_v=std::is_invocable<Fn,ArgTypes...>::value;template<class R,class Fn,class...ArgTypes>inlineconstexpr bool is_invocable_r_v=std::is_invocable_r<R,Fn,ArgTypes...>::value;template<class Fn,class...ArgTypes>inlineconstexpr bool is_nothrow_invocable_v=std::is_nothrow_invocable<Fn,ArgTypes...>::value;template<class R,class Fn,class...ArgTypes>inlineconstexpr bool is_nothrow_invocable_r_v=std::is_nothrow_invocable_r<R,Fn,ArgTypes...>::value;

其实说的简单点就是在提供基础的调用参数的检查的检查上,后面的接口增加了对返回值、异常抛出的检查,这也符合人们的认知习惯。

其实现的具体代码如下:

template<typename _Fn,typename..._ArgTypes>struct__is_invocable:__is_invocable_impl<__invoke_result<_Fn,_ArgTypes...>,void>::type{};template<typename _Fn,typename..._ArgTypes>structis_invocable:__is_invocable_impl<__invoke_result<_Fn,_ArgTypes...>,void>::type{static_assert(std::__is_complete_or_unbounded(__type_identity<_Fn>{}),"_Fn must be a complete class or an unbounded array");static_assert((std::__is_complete_or_unbounded(__type_identity<_ArgTypes>{})&&...),"each argument type must be a complete class or an unbounded array");};template<typename _Result,typename _Ret,bool=is_void<_Ret>::value,typename=void>struct__is_invocable_impl:false_type{using __nothrow_type=false_type;// For is_nothrow_invocable_r};// Used for valid INVOKE and INVOKE<void> expressions.template<typename _Result,typename _Ret>struct__is_invocable_impl<_Result,_Ret,/* is_void<_Ret> = */true,__void_t<typename _Result::type>>:true_type{using __nothrow_type=true_type;// For is_nothrow_invocable_r};template<typename _Result,typename _Ret>struct__is_invocable_impl<_Result,_Ret,/* is_void<_Ret> = */false,__void_t<typename _Result::type>>{private:// The type of the INVOKE expression.// Unlike declval, this doesn't add_rvalue_reference, so it respects// guaranteed copy elision.statictypename _Result::type_S_get()noexcept;template<typename _Tp>staticvoid_S_conv(_Tp)noexcept;// This overload is viable if INVOKE(f, args...) can convert to _Tp.template<typename _Tp,bool _Check_Noex=false,typename=decltype(_S_conv<_Tp>(_S_get())),bool _Noex=noexcept(_S_conv<_Tp>(_S_get()))>static__bool_constant<_Check_Noex?_Noex:true>_S_test(int);template<typename _Tp,bool=false>staticfalse_type_S_test(...);public:// For is_invocable_rusing type=decltype(_S_test<_Ret>(1));// For is_nothrow_invocable_rusing __nothrow_type=decltype(_S_test<_Ret,true>(1));};

上面的代码看起来有点熟悉,在前面“SFINAE的技巧应用”中的第一个例子是不是有些类似?不过,不同的编译器中可能实现有细节上的不同,大家在看代码时需要明白这一点。

三、技术分析

std::is_invocable的内部实现仍然与SFINAE技术紧密相关,也就是说在调用 std::is_invocable<Fn, Args…> 时,编译器会尝试在编译时构造一个对可调用函数对象Fn的调用,同时对变参 Args…进行处理。如果此调用检测通过,则std::is_invocable<Fn, Args…>::value将为 true;否则,将为 false。当然,变参的存在,使得这其中肯定有引用折叠和完美转发的情况。这也正好是上面的实现代码的部分。
在前面的学习中,大家已经明白了decltype和declval的用法,特别是后者,可以不需要创建一个真正的实例来获取相关的类型。二者配合就可以实现参数和返回类型的检测。它们两个在SFINAE中的应用是十分普遍的。更详细的可以分析一下具体的实现代码就会明白其中的道理。

四、应用场景和限制

其实看到它的说明和源码就会明白,它的主要应用场景就是在模板编程中,特别是元编程。主要包括:

  1. 编译时代码安全的控制检查,如函数的签名检查
  2. 对异常安全的控制,这也是SFINAE本身的特点
  3. 模板编程和元编程中的可调用对象检查
    需要注意的是,std::is_invocable系列中,会自动隐式的处理参数的转换。另外在处理函数指针时,要注意普通函数指针和类成员函数指针的具体的应用方法。在使用异常相关的接口时还要保证操作不抛出异常。

五、例程

具体的用法如下:

#include<iostream>#include<type_traits>class Demo{public:intcheckFunc(intd){returnd;}staticintstaticCheckFunc(intd){returnd*d;}};voidtest(){// check non-static member functionbool b1=std::is_invocable_r<int,decltype(&Demo::checkFunc),Demo*,int>::value;std::cout<<"checkFunc check result:"<<b1<<std::endl;bool b2=std::is_invocable_r<int,decltype(&Demo::checkFunc),Demo&,int>::value;std::cout<<"checkFunc check result:"<<b2<<std::endl;// check static member functionbool b3=std::is_invocable_r<int,decltype(&Demo::staticCheckFunc),int>::value;std::cout<<"staticCheckFunc check result:"<<b3<<std::endl;}autofunc2(char)->int(*)(){returnnullptr;}intmain(){test();static_assert(std::is_invocable_v<int()>);static_assert(not std::is_invocable_v<int(),int>);static_assert(std::is_invocable_r_v<int,int()>);static_assert(not std::is_invocable_r_v<int*,int()>);static_assert(std::is_invocable_r_v<void,void(int),int>);static_assert(not std::is_invocable_r_v<void,void(int),void>);static_assert(std::is_invocable_r_v<int(*)(),decltype(func2),char>);static_assert(not std::is_invocable_r_v<int(*)(),decltype(func2),void>);}

这种接口的代码没有什么可分析的,大家看看就明白了。重点看看函数内部函数的调用检测的情况。

六、总结

std::is_invocable系列接口是C++17库中统一函数调用参数检查的标准方式。它提供了在编译期的安全检查的机制,降低了开发的复杂度,提高了代码开发的效率。在模板编程特别是元编程中,可以有效的提高代码的安全性和健壮性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/5 9:26:45

内网渗透靶场实操命令速查表

本速查表基于之前的 VulhubMetasploitable 2 靶场实操清单&#xff0c;涵盖从外网突破到持久化的全流程命令&#xff0c;可直接复制执行。环境说明&#xff1a;攻击机 Kali&#xff08;192.168.56.101&#xff09;、跳板机 Metasploitable 2&#xff08;192.168.56.102&#xf…

作者头像 李华
网站建设 2026/3/7 9:52:39

从零开始做一个最简单的CNN实例

一、CNN基本概念 站内已经有详细的教程 【深度学习】一文搞懂卷积神经网络&#xff08;CNN&#xff09;的原理&#xff08;超详细&#xff09;_卷积神经网络原理-CSDN博客、 二、完成一个简单实例需要掌握什么 1.张量基本操作 我们将张量基本操作分为 4 个层次 &#xff0…

作者头像 李华
网站建设 2026/2/28 2:42:43

《创业之路》-741-CTO的核心能力是前瞻性和技术预见能力,对未来机会方向的洞察能力

“CTO的核心能力是前瞻性和技术预见能力&#xff0c;是对未来机会方向的洞察能力。”这不仅是对技术领导者的深刻理解&#xff0c;更是对企业能否穿越周期、持续创新的关键判断。下面我们来系统深化这一观点&#xff0c;将其升华为一套 “CTO前瞻性能力模型” ——不仅说明“是…

作者头像 李华
网站建设 2026/3/10 10:03:32

喷砂除锈设备工艺流程是什么?| 广东鑫百通喷砂机厂家

喷砂除锈设备标准工艺流程&#xff1a;五步实现完美基材 喷砂除锈设备工艺远非简单的“用砂子打表面”&#xff0c;而是一套严谨、高效的系统工程。规范的工艺流程是确保处理质量、控制成本与保障安全的核心。 喷砂除锈设备标准流程可精炼为以下5个关键步骤。 第一步&#xff1…

作者头像 李华
网站建设 2026/3/3 12:58:39

创客匠人:从个人IP到知识变现,如何构建可持续的内容生态?

在知识付费与内容创业浪潮中&#xff0c;越来越多的人开始意识到&#xff1a;单纯的内容输出已不足以构建长期竞争力。无论是讲师、咨询师、行业专家&#xff0c;还是创业者&#xff0c;都面临一个共同问题&#xff1a;如何从“被看见”走向“被选择”&#xff0c;并最终实现可…

作者头像 李华