应使用std::result_of::type,必须对每个参数用std::declval,函数类型f需可调用且不能是重载名或未定义模板别名。

std::result_of 在 C++11/14 中怎么写才不报错?
它早被弃用了,但老项目里躲不开。关键不是“怎么用”,而是“怎么写才让编译器不吐 std::result_of<f>::type</f> 未定义”。
常见错误现象:error: 'type' in 'struct std::result_of<...>' does not name a type</...> —— 这几乎全是参数类型没加 std::declval 导致的。
- 必须对每个参数用
std::declval<arg>()</arg>,不能直接写Arg;比如std::result_of<f double>::type</f>是错的,得写成std::result_of<f>(), std::declval<double>())>::type</double></f> - 函数类型
F必须可调用:普通函数指针、成员函数指针、functor 类型都行,但不能是重载函数名(会歧义),也不能是未定义的模板别名 - 如果
F是成员函数指针,第一个参数得是对象(或指针)类型,例如std::result_of<decltype int>::type</decltype>要写成std::result_of<decltype>(), std::declval<int>())>::type</int></decltype>
为什么 std::result_of 会被 std::invoke_result 替代?
根本原因:std::result_of 的 SFINAE 友好性差,且语义模糊 —— 它试图模拟“调用”,但实际依赖的是一个过时的可调用性检测规则(C++11 初版),遇到引用折叠、cv 限定符、返回引用等场景容易崩。
典型兼容性影响:
立即学习“C++免费学习笔记(深入)”;
-
std::result_of<f></f>在某些编译器(如旧版 GCC)里无法推导右值引用调用结果 - 对
noexcept函数或返回auto的 lambda,std::result_of常静默失败,而std::invoke_result明确支持 - C++17 起,
std::result_of标准中被标记为 deprecated,Clang 10+ 和 GCC 9+ 默认开启 -Wdeprecated-declarations 警告
在 C++14 项目里安全迁移到 invoke_result 的最小改动
不是所有地方都能立刻升 C++17,但可以靠 std::invoke_result_t 的别名兼容层绕过去 —— 关键是别自己实现,用标准库已提供的兜底。
实操建议:
- 头文件必须加
#include <type_traits></type_traits>(C++11 就有,但std::invoke_result需 C++17;不过你可以用条件编译 + 自定义别名) - 若编译器支持 C++17,直接用
std::invoke_result_t<f args...></f>,它不要求std::declval,参数类型直写即可 - 若只能用 C++14,定义兼容宏:
#if __cplusplus >= 201703L using result_type = std::invoke_result_t<F, Args...>; #else using result_type = typename std::result_of<F(std::declval<Args>()...)::type; #endif
最容易被忽略的坑:std::result_of 对 void 返回值的处理
它能推导出 void,但一旦你拿这个 type 去做 sizeof 或模板特化,就可能触发未定义行为 —— 因为某些老标准库实现里,std::result_of<...>::type</...> 对 void 场景没做充分约束。
真实使用场景中,这会导致:
- 用
std::result_of推导后接std::enable_if_t<:is_same_v void>></:is_same_v>,结果 SFINAE 失效,编译器报硬错误而非静默丢弃 - 在 trait 检测中嵌套使用(比如判断某函数是否返回 void),应优先改用
std::is_void_v<:invoke_result_t args...>></:invoke_result_t>,更稳 - 如果必须用
std::result_of,记得加一层std::decay_t再判断:std::is_void_v<:decay_t std::result_of>::type>></:decay_t>
越老的代码越容易在这里卡住,因为没人想到 void 也会让类型推导变脆弱。










