You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:28:09