• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2026-01-31 19:38 Aet 隐藏边栏 |   抢沙发  1 
文章评分 1 次,平均分 5.0

概述

  1. C++17 引入的一个强大的函数调用工具,它提供了一种统一的方式来调用各种可调用对象
  2. 作用目标
    1. 普通函数
    2. 成员函数
    3. 成员变量
    4. 函数对象(包括 lambda
    5. 任何重载了 operator() 的对象
  3. 为什么需要 std::invoke
    1. C++17 之前,调用不同类型的可调用对象需要不同的语法
    2. std::invoke 提供了统一的调用方式,这在泛型编程中非常重要

使用场景

普通函数

成员函数

  1. std::invoke 最强大的特性之一,它可以自动处理对象、指针和引用

访问成员变量

调用函数对象和 Lambda

高级应用

泛型包装器

  1. std::invoke 在实现泛型包装器时特别有用

实现类似 std::apply 的功能

回调系统

相关工具类型

std::invoke_result

  1. 用于获取调用结果的类型

std::is_invocable

  1. 检查是否可以用给定参数调用

实现原理

源码

无参数版本

  1. 尾置返回类型
    1. decltype 推导调用 _Obj() 的返回类型
    2. static_cast<_Callable&&> 是完美转发的手动实现

  1. noexcept 规范
    1. 外层 noexcept:声明函数的异常规范
    2. 内层 noexcept:检查表达式是否不抛异常
    3. 这是 noexcept 传播:如果调用不抛异常,invoke 也不抛

  1. static_cast<_Callable&&>
    1. 这不是简单的类型转换
    2. _CallableT&(左值引用)时,_Callable&& 变成 T& &&,折叠为 T&
    3. _CallableT(非引用)时,_Callable&& 就是 T&&(右值引用)
    4. 所以它是完美转发的核心机制

带参数版本

  1. 函数签名
    1. _Callable&&:被调用的对象(函数、成员函数指针等)
    2. _Ty1&&:第一个参数(特殊处理,因为可能是对象)
    3. _Types2&&...:剩余参数包

  1. 为什么要单独提取第一个参数
    1. 因为调用成员函数时,第一个参数是对象

  1. 返回类型和 noexcept
    1. 使用 _Invoker1 辅助类来决定返回类型和异常规范
    2. _Invoker1 会分析 _Callable_Ty1 的类型,确定调用策略

  1. _Functor(普通函数对象)
    1. 调用普通函数、lambda、函数对象

  1. _Pmf_object(成员函数指针 + 对象)
    1. .* 是成员指针运算符
    2. (obj.*pmf)(args...) 通过对象调用成员函数

  1. _Pmf_refwrap(成员函数指针 + reference_wrapper
    1. 处理 std::refstd::cref
    2. 为什么需要特殊处理
      reference_wrapper 不是对象本身,需要调用 .get() 获取真正的引用

  1. _Pmf_pointer(成员函数指针 + 指针)
    1. 语法: ((*ptr).*pmf)(args...)
    2. 为什么要先解引用
      因为 .* 运算符要求左操作数是对象,不能是指针

  1. _Pmd_object(成员变量指针 + 对象)
    1. 访问成员变量

  1. _Pmd_refwrap(成员变量指针 + reference_wrapper
    1. MSVC 在某些情况下无法正确处理 reference_wrapper.get().*member,需要先存储引用

  1. _Pmd_pointer(成员变量指针 + 指针)
    1. 通过指针访问成员变量

_Invoker1

  1. todo

其他

_EXPORT_STD

  1. MSVC 的内部宏,用于控制符号导出
  2. 在模块化编程(C++20 modules)中标记需要导出的符号

decltypeauto

  1. auto:会执行表达式,然后根据结果推导类型
    1. 但类型的确认是在编译期,根据的是表达式的签名
    2. 表达式的求值是在运行期,两者不矛盾
  2. decltype:不会执行表达式,只在编译期分析类型
    1. 不存在运行期的事情
特性 auto decltype
推断方式 类似模板参数推断,会退化(decay 精确保留表达式的类型
引用处理 忽略顶层引用 保留引用
const 处理 忽略顶层 const 保留 const
用途 变量声明 类型查询
  1. 既然autodecltyp推断的类型都是编译期确定,那么为啥auto推断的类型会有decay现象
    1. C++11 设计 auto 时,为了一致性,让它的推导规则与模板参数推导完全相同
  2. 那么为什么模板参数会退化
    1. 本质是值传递,创建了一个独立的副本
  3. 总结: auto 退化而 decltype 不退化
    1. 设计目的不同
      auto:变量声明,模仿函数参数传递
      decltype:类型查询,必须精确
    2. 语义不同
      auto:创建副本(退化是副作用)
      decltype:查询类型(不能有副作用)
    3. 历史原因
      auto:继承模板推导规则(一致性)
      decltype:新引入的类型查询工具(独立设计)

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享