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

如何简洁传递返回void的C++可调用对象执行结果?

问题:传递返回void的可调用对象执行结果的简洁实现

在C++中,若尝试将返回void的可调用对象的执行结果直接传递给函数,会触发编译错误。以下是简化示例:

#include <iostream>

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(void){
    std::cout << "void\n";
}

template <typename Callable>
void bar(Callable c){
    foo(c());
}

int main() 
{
    bar([](){
        return 42;
    });

    bar([](){});

    return 0;
}

编译时会报如下错误:

<source>: In instantiation of 'void bar(Callable) [with Callable = main()::<lambda()>]':
<source>:23:8:   required from here
<source>:14:10: error: invalid use of void expression
   14 |     foo(c());
      |         ~^~
ASM generation compiler returned: 1
<source>: In instantiation of 'void bar(Callable) [with Callable = main()::<lambda()>]':
<source>:23:8:   required from here
<source>:14:10: error: invalid use of void expression
   14 |     foo(c());
      |         ~^~

现有SFINAE实现方案

你已实现了一套SFINAE方案,但代码较为繁琐:

#include <type_traits>
#include <utility>

struct void_result{};

template <typename Result>
struct _get_result
{
    template <typename Callable, typename ... Args>
    constexpr decltype(auto) operator()(Callable c, Args &&... args) const noexcept {
        return c(std::forward<Args>(args)...);
    }
};

template <>
struct _get_result<void>
{
    template <typename Callable, typename ... Args>
    constexpr void_result operator()(Callable c, Args &&... args) const noexcept {
        c(std::forward<Args>(args)...);
        return {};
    }
};

template <typename Callable, typename ... Args>
constexpr decltype(auto) voidable(Callable c, Args && ... args) noexcept {
    return _get_result<std::invoke_result_t<Callable, Args...>>{}(c, std::forward<Args>(args)...);
}

#include <iostream>

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(void_result){
    std::cout << "void\n";
}

template <typename Callable, typename ... Args>
void bar(Callable c, Args && ...args){
    foo(voidable(c, std::forward<Args>(args)...));
}

int main() 
{
    bar([](){
        return 42;
    });

    bar([](){});

    return 0;
}

更简洁的实现方式

在C++17及以上版本中,可利用if constexpr结合std::invoke_result_t直接在逻辑内区分处理,无需复杂的模板特化结构:

方案1:调整foo重载,匹配无参调用

将原void foo(void)修改为更符合C++风格的无参形式,直接在bar内分支处理:

#include <iostream>
#include <type_traits>

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(){
    std::cout << "void\n";
}

template <typename Callable>
void bar(Callable c){
    if constexpr (std::is_void_v<std::invoke_result_t<Callable>>) {
        c();
        foo();
    } else {
        foo(c());
    }
}

int main() 
{
    bar([](){ return 42; });
    bar([](){});
    return 0;
}

方案2:保留原重载,简化包装逻辑

若需保留foo(void_result)的重载,可通过单个包装函数替代原有的模板特化:

#include <iostream>
#include <type_traits>
#include <utility>

struct void_result{};

template <typename Callable, typename... Args>
constexpr auto wrap_void(Callable&& c, Args&&... args) {
    if constexpr (std::is_void_v<std::invoke_result_t<Callable, Args...>>) {
        std::forward<Callable>(c)(std::forward<Args>(args)...);
        return void_result{};
    } else {
        return std::forward<Callable>(c)(std::forward<Args>(args)...);
    }
}

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(void_result){
    std::cout << "void\n";
}

template <typename Callable, typename ... Args>
void bar(Callable c, Args && ...args){
    foo(wrap_void(std::forward<Callable>(c), std::forward<Args>(args)...));
}

int main() 
{
    bar([](){ return 42; });
    bar([](){});
    return 0;
}

两种方案均通过编译期分支替代了繁琐的模板特化,代码更简洁直观。


内容的提问来源于stack exchange,提问作者Sergey Kolesnik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:25:48