news 2026/6/15 18:31:58

跟我学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/6/15 12:29:18

不属于五种算法就无需备案?关于算法备案的常见误区解析

在AI快速发展的今天&#xff0c;算法已成为互联网服务的核心驱动力。从个性化推荐到智能决策&#xff0c;算法深度嵌入我们的日常生活。然而&#xff0c;随着其广泛应用&#xff0c;算法带来的潜在风险——如“大数据杀熟”、算法歧视、信息茧房等问题——也引发了监管部门的高…

作者头像 李华
网站建设 2026/6/15 13:30:43

计算机毕业设计springboot基于SpringMVC的篮球俱乐部在线平台的设计与实现 基于 Spring Boot 的篮球俱乐部在线管理系统开发与实践 Spring Boot 框架下篮球俱乐部信息

计算机毕业设计springboot基于SpringMVC的篮球俱乐部在线平台的设计与实现8z58s9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着互联网技术的飞速发展&#xff0c;传统篮球…

作者头像 李华
网站建设 2026/6/13 18:49:37

《创业之路》-742-技术创业者面临哪些问题?

技术创业者在创办科技类企业过程中&#xff0c;通常会面临一系列独特而复杂的挑战。这些问题贯穿于创业的各个阶段&#xff0c;涉及技术、市场、资金、团队和管理等多个维度。以下是技术创业者常见且关键的问题&#xff1a;1. 技术与市场需求脱节&#xff08;“为技术而技术”&…

作者头像 李华
网站建设 2026/6/15 2:47:53

小程序计算机毕设之基于springboot+微信小程序的闲置物品处置平台的设计与实现基于微信小程序的闲置物品交易平台设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/15 14:16:35

小程序毕设选题推荐:基于小程序的高校学生公寓道闸管理平台的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华