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 的结果类型"。

相关推荐
j_xxx404_3 小时前
C++算法:哈希表(简介|两数之和|判断是否互为字符重排)
数据结构·c++·算法·leetcode·蓝桥杯·力扣·散列表
脱氧核糖核酸__3 小时前
LeetCode热题100——189.轮转数组(题解+答案+要点)
数据结构·c++·算法·leetcode
coding者在努力4 小时前
被n整除的n位数
c++·算法
赵药师4 小时前
Win11下的VS2022 配置RGBD435i 相机
c++·vs2022·windows11·rgbd435i
量子炒饭大师4 小时前
【C++ 进阶】Cyber霓虹掩体下的代码拟态——【面向对象编程 之 多态】一文带你搞懂C++面向对象编程中的三要素之一————多态!
开发语言·c++·多态
伴我与影4 小时前
【记录】复现论文 Dftpav
c++·docker
风曦Kisaki4 小时前
# LAMP 架构 + Discuz! 论坛实战笔记
笔记·架构
Hical_W5 小时前
深入学习CPP17_PMR
c++·学习
xuanwenchao5 小时前
ROS2学习笔记 - 1、编写运行第一个程序
笔记·学习