std::result_of 在 C++17 被弃用、C++20 移除,应统一替换为 SFINAE 安全且语义清晰的 std::invoke_result_t;后者严格遵循 std::invoke 规则处理参数、cv-qualifier 和引用类型,支持所有可调用对象,是标准唯一正统方案。

std::result_of 在 C++17 被弃用,直接换用 std::invoke_result
std::result_of 已在 C++17 中被标记为 deprecated,C++20 彻底移除。它不能用于 SFINAE 友好场景,且对 cv-qualifier 和引用类型推导不严谨 —— 比如 std::result_of 实际会尝试调用 F::operator() 的 const 重载(若存在),行为不可靠。
替代方案只有一个:std::invoke_result,它基于 std::invoke 协议定义,语义更清晰、SFINAE 安全、支持所有可调用对象(函数指针、成员函数指针、lambda、functor)。
-
std::result_of→ 改写为std::invoke_result_t - 注意:
std::invoke_result是类模板,需用_t别名(如std::invoke_result_t)获取类型,而非::type - 旧写法
typename std::result_of在 C++17 编译器上会触发 -Wdeprecated-declarations 警告::type
std::invoke_result_t 的参数传递方式影响推导结果
和 std::result_of 不同,std::invoke_result 严格按 std::invoke 规则处理参数:左值被转发为左值引用,右值被移动,cv-qualifier 保留。这意味着传参形式直接决定能否匹配目标可调用对象。
- 若函数接受
int&&,传std::move(x)才能成功推导;传x(左值)会失败 - 若 functor 的
operator()是 const 限定的,std::invoke_result_t合法,但std::invoke_result_t可能失败 - 成员函数指针需显式写出对象类型:如
std::invoke_result_t,不能省略S
常见错误现象:std::invoke_result_t 编译失败,但 func 明明能用 —— 很可能因为 func 是左值,而它的 operator() 非 const,此时应改用 std::invoke_result_t。
立即学习“C++免费学习笔记(深入)”;
从 C++14 迁移到 C++17+ 时容易漏掉的细节
很多老代码用 std::result_of 做 traits 判断,升级编译器后没报错但逻辑出偏,问题往往藏在类型别名嵌套或模板特化里。
- 宏定义中隐藏的
std::result_of(如某些 Boost 兼容层)需手动替换,否则 C++20 下直接编译不过 - 自定义 traits 类模板若继承自
std::result_of<...>,必须重写为继承std::invoke_result<...>并用::type提取(std::invoke_result本身无::type,得用typename std::invoke_result<...>::type) - Clang 和 GCC 对
std::result_of的弃用警告默认不开启(如 GCC 需加-Wdeprecated-declarations),容易错过迁移时机
为什么不用 std::declval + decltype 模拟?
有人试图用 decltype(std::declval 替代,看似等价,但有严重缺陷:
- 不支持成员函数指针调用语法(
obj.*ptr或(obj.*ptr)()无法在 unevaluated context 中合法写出) - 对重载函数名(非指针)无法解析,
std::declval无法绑定到具体重载() - 不处理
std::reference_wrapper自动解引用逻辑,而std::invoke协议明确支持 - 性能上无差异,但可维护性差 ——
std::invoke_result是标准规定的唯一正统方案
真正复杂的地方在于:不是“能不能推导出返回类型”,而是“是否以和实际调用一致的方式推导”。只要涉及成员指针、refwrap、重载集,手写 decltype 就大概率漏 case。标准库替你把 invoke 的所有分支都覆盖了,别自己造轮子。










