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

C++自定义async实现报错:无匹配__invoke_r函数调用

问题:自定义async实现中std::packaged_task构造时的编译错误及返回值推导优化

我用简化代码实现了基于自定义async的http_GET函数,这个async接收回调参数,创建临时entity对象,可通过get_future()获取与回调返回值匹配的std::future。现在在entity对象构造阶段出现编译错误:内部函数__invoke_r找不到匹配的重载。观察错误信息发现GCC已正确推导回调返回值为response,但不清楚问题所在。同时希望获取更简便的回调返回值推导方法。

代码

#include <type_traits>
#include <concepts>
#include <memory_resource>
#include <functional>
#include <tuple>
#include <cstdio>
#include <utility>
#include <type_traits>
#include <future>

using allocator_t = std::pmr::polymorphic_allocator<std::byte>;


template <typename R>
struct entity
{

    template <typename Cb> requires (std::constructible_from<std::packaged_task<R()>, Cb>)
    entity(Cb&& fn, allocator_t allocator = {})
        :   task_{ std::move(fn) }
    {

    }

    auto get_future() -> std::future<R> {
        return std::future<R>{};
    }

    std::packaged_task<R()> task_;
};

template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_alloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) -> std::future<decltype(fn(std::get<I>(std::move(tuple))...))>
{
    return entity<decltype(fn(std::get<I>(std::move(tuple))...))>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
            fn(std::forward<decltype(bargs)>(bargs)...);
        }, std::get<std::index_sequence<I...>::size()>(std::move(tuple))).get_future();
}

template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_noalloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) -> std::future<decltype(fn(std::get<I>(std::move(tuple))...))>
{
    return entity<decltype(fn(std::get<I>(std::move(tuple))...))>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
            fn(std::forward<decltype(bargs)>(bargs)...);
        }).get_future();
}

template<typename Cb, typename... Args>
auto async2(Cb&& fn, Args&&... args)
{
    auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
    constexpr std::size_t cnt = sizeof...(Args);

    if constexpr (std::same_as<std::remove_cvref_t<std::tuple_element<cnt-1, decltype(tuple)>>, allocator_t>) {
        printf("with allocator\n");
        return async_impl_alloc(std::forward<Cb>(fn), std::forward<decltype(tuple)>(tuple), std::make_index_sequence<cnt-1>{});
    } else {
        printf("without allocator\n");
        return async_impl_noalloc(std::forward<Cb>(fn), std::forward<decltype(tuple)>(tuple), std::make_index_sequence<cnt>{});
    }
}

struct response
{};

auto http_GET(std::pmr::string url)
{
    return async2([](const std::pmr::string& url){
        return response{};
    }, std::move(url));
}

int main()
{
    allocator_t allocator;

    auto fut = http_GET("http://my.url.com");
}

错误信息

/opt/compiler-explorer/gcc-trunk-20230123/include/c++/13.0.1/future:1491:41: error: no matching function for call to '__invoke_r<response>(async_impl_noalloc<http_GET(std::pmr::string)::<lambda(const std::pmr::string&)>, std::__cxx11::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> >&&, 0>(http_GET(std::pmr::string)::<lambda(const std::pmr::string&)>&&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::pmr::polymorphic_allocator<char> >&&>, std::index_sequence<0>)::<lambda()>&)'
 1491 |             return std::__invoke_r<_Res>(_M_impl._M_fn,
      |                    ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
 1492 |                                          std::forward<_Args>(__args)...);
      |                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
解决方案

1. 编译错误原因及修复

错误根源在于传递给std::packaged_task<R()>的lambda表达式没有返回值:lambda内部调用了fn(...)但未将结果返回。std::packaged_task<R()>要求可调用对象必须返回R类型,而你的lambda实际返回void,导致__invoke_r无法找到匹配的重载(它需要将返回值转换为response,但lambda无返回值)。

修改lambda,添加return语句即可修复:

// 在async_impl_noalloc和async_impl_alloc中修改lambda
[fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
    return fn(std::forward<decltype(bargs)>(bargs)...); // 添加return
}

2. 简化回调返回值推导的方法

当前代码用decltype(fn(std::get<I>(std::move(tuple))...))推导返回值,虽可行但可更简洁:

方法一:使用std::invoke_result_t

直接利用标准库类型 trait 推导可调用对象的返回值,代码更清晰:

template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_noalloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) 
-> std::future<std::invoke_result_t<Cb, Args...>> {
    using R = std::invoke_result_t<Cb, Args...>;
    return entity<R>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
        return fn(std::forward<decltype(bargs)>(bargs)...);
    }).get_future();
}

方法二:让entity配合自动推导(C++20+)

简化entity的构造逻辑,结合std::invoke_result_t直接推导返回类型:

// 简化entity构造,移除冗余requires(packaged_task会自行检查兼容性)
template <typename R>
struct entity
{
    template <typename Cb>
    entity(Cb&& fn, allocator_t allocator = {})
        : task_{ std::move(fn) } {}

    // 修正:返回task关联的future而非空future
    std::future<R> get_future() { return task_.get_future(); }

    std::packaged_task<R()> task_;
};

// 用auto推导返回值,配合std::invoke_result_t简化类型推导
template <typename Cb, typename... Args, std::size_t... I>
auto async_impl_noalloc(Cb&& fn, std::tuple<Args...> tuple, std::index_sequence<I...>) {
    using R = std::invoke_result_t<Cb, std::tuple_element_t<I, std::tuple<Args...>>...>;
    return entity<R>([fn = std::forward<Cb>(fn), ... bargs = std::get<I>(std::move(tuple))]() mutable {
        return fn(std::forward<decltype(bargs)>(bargs)...);
    }).get_future();
}

额外注意点

原代码中entity::get_future()返回空的std::future<R>是错误的,应该返回task_.get_future(),否则future无法获取任务的执行结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 04:25:39