news 2026/6/21 17:35:01

跟我学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/22 17:07:16

训练速度飙升5倍!Unsloth动态量化让大模型微调告别“卡脖子”

还在为大模型微调的速度慢、显存爆而烦恼&#xff1f;Unsloth的黑科技让你在消费级GPU上也能轻松玩转百亿参数模型&#xff01; 摘要 Unsloth 是一个革命性的大型语言模型&#xff08;LLM&#xff09;高效微调框架。它通过独家手动推导并编写的GPU内核&#xff0c;在不改变硬件…

作者头像 李华
网站建设 2026/6/22 13:40:10

Nmap深度解析:信息收集

概述 (Overview) Nmap (“Network Mapper”) 是一款开源且功能强大的网络扫描工具&#xff0c;广泛用于网络发现和安全审计。它利用原始 IP 报文来发现网络上的主机、探测这些主机开放的端口、确定端口上运行的服务及其版本、推测目标操作系统&#xff0c;并可通过 Nmap 脚本引…

作者头像 李华
网站建设 2026/6/22 16:40:44

【收藏】零基础学大模型:2025年小白入门完全指南

本文详细介绍AI大模型的多种类型&#xff08;语言、向量、视觉、多模态等&#xff09;、获取渠道&#xff08;官方、第三方付费/免费、逆向工程&#xff09;及实用方法&#xff08;提示词优化、智能体应用等&#xff09;。为2025年AI初学者提供零基础学习指南&#xff0c;帮助普…

作者头像 李华
网站建设 2026/6/22 16:25:47

小程序毕设选题推荐:基于SpringBoot+微信小程序的微信电影院票务系统管理系统设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

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

作者头像 李华
网站建设 2026/6/20 19:01:41

高通跃龙QCS6490平台视频录制与上传(1): 系统环境搭建指南

本文为系列教程的第一篇&#xff0c;主要介绍在高通QCS6490平台的Linux系统中搭建视频录制与上传功能所需的基础环境配置&#xff0c;涵盖系统工具安装、多媒体框架配置及开发环境准备。1. 平台概述与应用场景 高通跃龙QCS6490是一款基于ARM架构的高性能嵌入式平台&#xff0c;…

作者头像 李华