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

C++协程Signal/Slot库co_yield模板推导失败问题求助

C++协程Signal/Slot库的模板推导失败问题

我尝试基于C++协程实现一个基础的Signal/Slot库,核心逻辑是:Signal成员可连接同签名的Slot,当co_yield某个Signal时触发已注册的Slot。

现有代码实现

Signal类定义

template<typename... T>
class Signal {
public:
    using slot_member = std::function<Slot(T...)>;
    struct SignalArgs {
        SignalArgs(std::span<slot_member> slots, T... args): args(args...), slots(slots)  {}
        std::tuple<T...> args;
        std::span<slot_member> slots;
    };

    SignalArgs operator()(T... args) {
        return SignalArgs(slots, args...);
    }
private:
    (...)
};

yield_value函数实现

执行co_yield my_sig(...)时,Promise会接收SignalArgs对象,对应的yield_value定义如下:

template<typename... T>
std::suspend_never yield_value(typename Signal<T...>::SignalArgs args)
{
    for (auto f: args.slots) {
        Slot s = std::apply(f(args.args));
        s();
    }
    return {};
}

报错信息

error: no matching member function for call to 'yield_value'
co_yield s(3, "lil");
^~~~~~~~
note: candidate function [with T = <>] not viable: no known conversion from 'Signal<int, const char *>::SignalArgs' to 'typename Signal<>::SignalArgs' for 1st argument
std::suspend_never yield_value(typename Signal<T...>::SignalArgs args)

问题原因

模板参数推导无法从Signal<T...>::SignalArgs这种嵌套类型反向推导出T...——这属于C++中的非推导上下文,编译器没办法从传入的Signal<int, const char*>::SignalArgs实例,反推外层Signal类的模板参数int, const char*。

解决方法

方法一:给SignalArgs添加嵌套类型,暴露模板参数

修改Signal类,让SignalArgs携带外层Signal的类型信息,这样yield_value可以直接从参数中推导所需类型:

template<typename... T>
class Signal {
public:
    using slot_member = std::function<Slot(T...)>;
    struct SignalArgs {
        // 添加嵌套类型,暴露外层Signal的模板参数信息
        using SignalType = Signal<T...>;
        using ArgsTuple = std::tuple<T...>;
        
        SignalArgs(std::span<slot_member> slots, T... args): args(args...), slots(slots)  {}
        std::tuple<T...> args;
        std::span<slot_member> slots;
    };

    SignalArgs operator()(T... args) {
        return SignalArgs(slots, args...);
    }
private:
    std::vector<slot_member> slots; // 补充缺失的成员定义
};

重写yield_value函数,利用嵌套类型推导:

template<typename Args>
std::suspend_never yield_value(Args args)
{
    using SignalType = typename Args::SignalType;
    using SlotFunc = typename SignalType::slot_member;
    
    for (SlotFunc f : args.slots) {
        // 修正原代码的std::apply调用错误:第一个参数是可调用对象,第二个是元组
        Slot s = std::apply(f, args.args);
        s();
    }
    return {};
}

方法二:将yield_value定义为Signal的友元函数

直接把yield_value作为Signal的友元,这样可以直接获取Signal的模板参数T...,避免推导问题:

template<typename... T>
class Signal {
public:
    using slot_member = std::function<Slot(T...)>;
    struct SignalArgs {
        SignalArgs(std::span<slot_member> slots, T... args): args(args...), slots(slots)  {}
        std::tuple<T...> args;
        std::span<slot_member> slots;
    };

    SignalArgs operator()(T... args) {
        return SignalArgs(slots, args...);
    }

    // 友元函数直接拿到T...参数,无需推导
    friend std::suspend_never yield_value(SignalArgs args) {
        for (auto f : args.slots) {
            Slot s = std::apply(f, args.args);
            s();
        }
        return {};
    }
private:
    std::vector<slot_member> slots;
};

额外注意

原代码中的std::apply(f(args.args))是错误调用,正确的std::apply用法是将可调用对象和元组作为两个独立参数传入,即std::apply(f, args.args)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:33:49