如何实现支持void及其他返回值类型的C++函数包装器?
问题描述
我实现了如下代码:
template<typename F, typename... Args> static auto run_func(F&& f, const string& msg, Args&&... args) -> decltype(f(args...)) { using T = decltype(f(args...)); cout << msg << endl; auto timer = Timer(); // 用于记录时间的工具 auto res = std::invoke(f, args...); // 此处在函数返回void时会失败 cout << "\t- time used: " << timer.time_duration() << endl; return res; }
这段代码仅在被包装函数f的返回值类型非void时能正常工作:
int func1(int a, int b) {return a + b;} void func2(int a, int b, int c) {cout << a + b + c << endl;} cout << run_func(func1, "执行加法", 1, 2); // 此调用正常 run_func(func2, "打印总和", 2, 3, 4); // 此调用失败
请问如何修改run_func,使其能够支持所有返回值类型的函数?
解决方法
方法一:用C++17的if constexpr编译期分支(推荐)
这是最简洁的实现方式,通过编译期判断返回类型是否为void,分别处理两种情况:
#include <type_traits> // 必须包含此头文件 template<typename F, typename... Args> static auto run_func(F&& f, const string& msg, Args&&... args) { cout << msg << endl; auto timer = Timer(); // 推导被调用函数的返回类型 using ReturnType = decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)); if constexpr (!std::is_void_v<ReturnType>) { // 非void返回:接收结果并返回 auto res = std::invoke(std::forward<F>(f), std::forward<Args>(args)...); cout << "\t- 耗时: " << timer.time_duration() << endl; return res; } else { // void返回:直接执行,无需接收结果 std::invoke(std::forward<F>(f), std::forward<Args>(args)...); cout << "\t- 耗时: " << timer.time_duration() << endl; } }
说明:
if constexpr会在编译期剔除无效分支,避免void类型赋值给变量的错误- 使用
std::forward实现完美转发,保证参数传递的效率和正确性 - 省略尾置返回类型,让编译器自动推导返回值
方法二:针对void返回类型重载函数(兼容C++11/14)
如果需要支持C++17之前的标准,可以通过SFINAE技术重载两个版本的函数:
#include <type_traits> // 处理非void返回类型的版本 template<typename F, typename... Args> auto run_func(F&& f, const string& msg, Args&&... args) -> typename std::enable_if< !std::is_void<decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...))>::value, decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...)) >::type { cout << msg << endl; auto timer = Timer(); auto res = std::invoke(std::forward<F>(f), std::forward<Args>(args)...); cout << "\t- 耗时: " << timer.time_duration() << endl; return res; } // 处理void返回类型的版本 template<typename F, typename... Args> auto run_func(F&& f, const string& msg, Args&&... args) -> typename std::enable_if< std::is_void<decltype(std::invoke(std::forward<F>(f), std::forward<Args>(args)...))>::value, void >::type { cout << msg << endl; auto timer = Timer(); std::invoke(std::forward<F>(f), std::forward<Args>(args)...); cout << "\t- 耗时: " << timer.time_duration() << endl; }
说明:
- 用
std::enable_if做SFINAE,编译器会根据函数返回类型自动选择对应的重载版本 - 同样使用完美转发保证参数传递的正确性
内容的提问来源于stack exchange,提问作者Zeyu Zhang CN
相关产品推荐
相关产品推荐

