定义
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_t和std::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
}
为什么这种写法更好?
-
一致性 :它与
std::invoke的逻辑完全匹配。只要std::invoke能跑通的代码,这个返回类型推导就绝对不会出错。 -
SFINAE 友好 :如果用户传入了错误的参数(比如参数个数不对),
std::invoke_result_t会导致模板匹配失败(替换失败),从而产生更干净的编译错误信息,而不是在函数体内部报错。 -
语义清晰 :它直接告诉读者:"这个函数的返回类型,就是调用
f的结果类型"。