基于if constexpr与SFINAE编译时控制分支:判断函数可调用性
在编译期判断函数可调用性并启用代码分支
刚好之前处理过类似的场景,我来给你捋清楚怎么弄~
首先要明确:C++17及以后,推荐直接用std::is_invocable_v系列特性,比std::result_of更直观、更符合你的需求。std::result_of在C++17已经被标记为废弃,所以优先用新的工具会更省心。
核心思路:用std::is_invocable_v作为if constexpr的判断条件
std::is_invocable_v<F, Args...>是一个编译期布尔常量,当可调用对象F能被参数类型Args...调用时,它的值为true,否则为false,完美适配if constexpr的编译期分支判断需求。
直接上代码例子
假设我们有几个重载的add函数,以及一些测试类型:
#include <type_traits> #include <iostream> struct A {}; struct B {}; // 两个基础类型的add重载 int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } // 没有add(A, B)的重载 template<typename T, typename U> void test_add_call() { if constexpr (std::is_invocable_v<decltype(add), T, U>) { // 这个分支只有在add能被T、U类型调用时才会被实例化 std::cout << "✅ add可以用T和U类型调用\n"; auto result = add(T{}, U{}); std::cout << "返回值类型:" << typeid(result).name() << "\n"; } else { // 这个分支在不可调用时才会生效,不会触发编译错误 std::cout << "❌ add无法用T和U类型调用\n"; } } int main() { test_add_call<int, int>(); // 可行,输出✅相关内容 test_add_call<double, double>(); // 可行,输出✅相关内容 test_add_call<A, B>(); // 不可行,输出❌相关内容 return 0; }
如果一定要用std::result_of怎么办?
如果你因为某些原因必须依赖std::result_of,可以通过SFINAE检测std::result_of_t<F(Args...)>是否存在,把这个存在性转换为编译期布尔值。比如用std::void_t做辅助:
#include <type_traits> // SFINAE辅助函数:如果result_of_t存在,返回true template<typename F, typename... Args, typename = std::void_t<std::result_of_t<F(Args...)>>> constexpr bool is_callable_with_result_of_impl(int) { return true; } // fallback版本:返回false template<typename F, typename... Args> constexpr bool is_callable_with_result_of_impl(...) { return false; } // 封装成常量模板 template<typename F, typename... Args> constexpr bool is_callable_with_result_of = is_callable_with_result_of_impl<F, Args...>(0); // 然后在if constexpr里用 if constexpr (is_callable_with_result_of<decltype(add), A, B>) { // 可调用分支 } else { // 不可调用分支 }
不过还是那句话,这个写法比std::is_invocable_v繁琐很多,没必要的话优先用标准库提供的现成工具。
额外补充:判断返回类型是否符合要求
如果你不仅要判断能否调用,还要判断返回类型是否是某个特定类型(比如是否是int),可以用std::is_invocable_r_v<Ret, F, Args...>,它会同时检查:
F能否被Args...调用;- 调用后的返回类型能否隐式转换为
Ret。
比如:
// 判断add(int, int)的返回是否能转成long if constexpr (std::is_invocable_r_v<long, decltype(add), int, int>) { // 满足条件 }
内容的提问来源于stack exchange,提问作者jfrohnhofen
相关产品推荐
相关产品推荐

