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

如何为template<typename... Args>模板函数创建扩展参数列表的对应类型

解决思路

核心是先从传入的TFunc中萃取固定的参数列表,因为同一实例化的CallManager对应的用户函数参数是固定的,不需要动态可变的参数模板。


完整实现代码

1. 函数类型萃取工具

用来提取用户函数的返回值、参数列表,支持拼接自定义参数生成新的函数类型:

#include <utility>
#include <type_traits>
#include <cstdio>

template<typename T>
struct function_traits;

// 针对函数指针的特化实现
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)>
{
    using return_type = Ret;
    static constexpr size_t args_count = sizeof...(Args);

    // 拼接自定义参数,生成类成员函数指针类型
    template<typename ClassType, typename... ExtraArgs>
    using class_member_func_with_extra = Ret(ClassType::*)(ExtraArgs..., Args...);
};

2. 修改后的CallManager实现

template<typename TFunc>
class CallManager {
    // 提前萃取用户函数的所有类型信息
    using traits = function_traits<TFunc>;
    TFunc user_function = nullptr;
    void* inner_state = nullptr;
    // 存储绑定的目标实例指针(成员函数调用必须绑定实例)
    const CallManager* target_instance = nullptr;

    // Wrapper类型现在是确定的,不需要再定义为模板别名
    using TWrapper = typename traits::template class_member_func_with_extra<CallManager, void* const>;
    TWrapper wrapper_from_another_instance = nullptr;

    // 直接用萃取的参数约束函数,不需要额外定义模板参数
    template<typename... Args>
    void* WrapUserFunction(void* const target, Args&&... func_args) {
        static_assert(std::is_invocable_v<TFunc, Args...>, "传入参数与用户函数不匹配");
        user_function(std::forward<Args>(func_args)...);
        return nullptr;
    }

    void setTargetExecutor(const CallManager* instance, TWrapper wrapper) {
        target_instance = instance;
        wrapper_from_another_instance = wrapper;
    }
public:
    void setFunction(TFunc func) {
        user_function = func;
    }

    template <typename... Args>
    void makeCall(Args&&... func_args) {
        // 成员函数指针需要绑定实例才能调用
        (target_instance->*wrapper_from_another_instance)(inner_state, std::forward<Args>(func_args)...);
    }

    void bindManager(const CallManager &next) {
        // 明确获取实例化后的成员函数地址
        setTargetExecutor(&next, &CallManager::WrapUserFunction);
    }
};

3. 测试用例

using myfunc = void(*)(char x, char y);

void myFunc(char x, char y) {
    printf("调用成功,参数:%d %d\n", x, y);
}

int main() {
    CallManager<myfunc> cmgr1;
    cmgr1.setFunction(myFunc);

    CallManager<myfunc> cmgr2;
    cmgr2.bindManager(cmgr1);

    cmgr2.makeCall(10, 15);
    return 0;
}

运行后输出:调用成功,参数:10 15,符合预期。


关键修改说明

  1. 类型萃取:通过function_traits从用户传入的TFunc中提前提取固定的参数列表,避免了运行时不确定参数类型的问题
  2. 固定Wrapper类型:原来的TWrapper是模板别名,无法直接用来声明成员变量,现在通过萃取得到的参数拼接成确定的成员函数指针类型,可以直接定义成员变量
  3. 成员函数调用修正:成员函数指针不能直接调用,必须绑定对应的实例,所以新增了target_instance成员存储绑定的CallManager实例

问题解答

  1. 如何为模板函数创建对应的类型?
    模板函数本身不是完整的可使用类型,必须先确定所有模板参数完成实例化才能得到具体类型。你这个场景下模板参数可以直接从类的模板参数TFunc萃取得到,不需要额外传参。如果是参数完全动态的场景,可以用std::function做类型擦除,存储任意可调用对象。

  2. 如何在用户函数类型基础上补充自定义参数?
    通过类型萃取拿到用户函数的参数包之后,在定义新的函数类型时,直接把你要添加的自定义参数放在参数列表的对应位置即可,就像示例中我们在参数列表最前面加了void* const target参数。


内容的提问来源于stack exchange,提问作者Alex A.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 10:42:03