C++笔记:std::invoke

定义

cpp 复制代码
template< class F, class... Args >
std::invoke_result_t<F, Args...>
invoke( F&& f, Args&&... args ) noexcept();

参数

|------|---|-----------------------|
| f | - | 要调用的可调用 (Callable) 对象 |
| args | - | 传递给 f 的参数 |

返回值

f 返回的值。

示例

cpp 复制代码
#include <functional>
#include <iostream>
#include <type_traits>
 
struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};
 
void print_num(int i)
{
    std::cout << i << '\n';
}
 
struct PrintNum {
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};
 
int main()
{
    // 调用自由函数
    std::invoke(print_num, -9);
 
    // 调用 lambda
    std::invoke([]() { print_num(42); });
 
    // 调用成员函数
    const Foo foo(314159);
    std::invoke(&Foo::print_add, foo, 1);
 
    // 调用(访问)数据成员
    std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n';
 
    // 调用函数对象
    std::invoke(PrintNum(), 18);
 
}

为什么需要它?

如果没有 std::invoke,针对不同的类型,你必须编写不同的调用代码。请看下表的对比:

可调用类型 传统调用语法 使用 std::invoke
普通函数 / Lambda func(args...) std::invoke(func, args...)
成员函数指针 (obj.*ptr)(args...) std::invoke(ptr, obj, args...)
成员变量指针 obj.*ptr std::invoke(ptr, obj)

std::invoke_result_t

std::invoke_result_tstd::invoke_result是与 std::invoke 配套使用的类型萃取(Type Traits)工具,同样在 C++17 中引入。它们的主要作用是在编译期预测:如果我用某些参数调用某个函数,返回值的类型会是什么。

cpp 复制代码
// 1. 基础模板类
template< class F, class... ArgTypes >
struct invoke_result;

// 2. 类型别名(最常用)
template< class F, class... ArgTypes >
using invoke_result_t = typename invoke_result<F, ArgTypes...>::type;
  • F: 可调用对象的类型。

  • ArgTypes...: 拟传递给该对象的参数类型列表。

  • std::invoke_result<F, Args...> : 是一个结构体。你需要通过 ::type 来获取最终的类型。它还继承了 std::type_identity,如果调用是不合法的(例如参数不匹配),则不会定义 type(这在 SFINAE 中非常有用)。

  • std::invoke_result_t<F, Args...> : 是 C++17 提供的模板别名,直接返回类型。它是 typename std::invoke_result<F, Args...>::type 的简写,用起来更清爽。

可以用它和invoke处理泛型函数传输功能

cpp 复制代码
#include <iostream>
#include <type_traits>
#include <string>

int func(double x) { return static_cast<int>(x); }

template <typename F, typename... Args>
auto wrapper(F&& f, Args&&... args) 
    -> std::invoke_result_t<F, Args...>{ // 明确指定返回类型
    // 使用 invoke_result_t 自动推导返回值类型
    using ReturnType = std::invoke_result_t<F, Args...>;
    
    std::cout << "Return type is: " << typeid(ReturnType).name() << std::endl;
    
    // 执行调用
    ReturnType result = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    std::cout << "Result: " << result << std::endl;
    return result;
}

int main() {
    // 推导 func(double) 的返回类型,应该是 int
    wrapper(func, 3.14); 
    
    // 也可以用于 Lambda
    auto my_lambda = [](int a) -> std::string { return std::to_string(a); };
    wrapper(my_lambda, 100); // ReturnType 将被推导为 std::string
}
为什么这种写法更好?
  1. 一致性 :它与 std::invoke 的逻辑完全匹配。只要 std::invoke 能跑通的代码,这个返回类型推导就绝对不会出错。

  2. SFINAE 友好 :如果用户传入了错误的参数(比如参数个数不对),std::invoke_result_t 会导致模板匹配失败(替换失败),从而产生更干净的编译错误信息,而不是在函数体内部报错。

  3. 语义清晰 :它直接告诉读者:"这个函数的返回类型,就是调用 f 的结果类型"。

相关推荐
王老师青少年编程18 分钟前
信奥赛C++提高组csp-s之FHQ Treap
c++·csp·平衡树·信奥赛·csp-s·提高组·fhq treap
星恒随风19 分钟前
Python 基础语法详解(一):从表达式、变量到数据类型
开发语言·笔记·python·学习
暴躁小师兄数据学院2 小时前
【AI大数据工程师特训笔记】第14讲:Linux操作系统与shell脚本
大数据·人工智能·笔记
QiLinkOS2 小时前
《打破“用爱发电”:一种基于 Gitee 与时间戳的开源权益分配机制探索》
c语言·数据结构·c++·科技·算法·gitee·开源
土狗TuGou2 小时前
SQL内功笔记 · 第8篇:事务的四大特性与隔离级别
数据库·笔记·后端·sql·mysql·oracle
Irissgwe2 小时前
c++STL--string类
c++·stl·string
Irissgwe3 小时前
c++类型转换
c++·类型转换·explicit·static_cast·const_cast·dynamic_cast·rtti
智者知已应修善业3 小时前
【51单片机用T0定时器方式1,实现0.5S的时间间隔实现第一次一个灯亮、第二次二个灯亮,直到全部灯亮,然后重复整个过程】2023-12-29
c++·经验分享·笔记·算法·51单片机
智者知已应修善业3 小时前
【51单片机4位静态数码管显示1234】2023-11-14
c++·经验分享·笔记·算法·51单片机
抓虾爪3 小时前
ST意法代理商粤科源兴丨LSM6DS3全系列现货库存,LSM6DS3TR-C当天可发
c++