如何将可变参数模板函数传递给index_filter的execute成员方法
核心问题定位
现有代码无法直接完成预期调用,根源来自两个语言规则限制和实现漏洞:
- C++不允许将函数模板直接作为模板模板参数传递:原
execute方法声明的template< template<int... fntps> class Fn形参,仅支持接收类模板/别名模板作为实参,而getter是函数模板,属于重载函数集合,没有具体类型,无法绑定到该形参位置。 - 原
execute的调用逻辑缺失了模板参数传递:即使成功传入模板,原代码直接调用fn(args...),没有将特化内部生成的rests...参数包作为显式模板参数传给目标函数,本质上没有完成函数实例化的步骤。
可行实现方案
所有方案均可以保留现有index_filter的索引生成逻辑、原getter函数的递归实现,仅需要做极小的适配改动,完全匹配预期的调用形式。
方案1:零开销函数标记包装(兼容C++17及以上标准)
- 新增一个通用的函数标记包装器,把函数模板绑定到一个有具体类型的常量上,实现无值传递
// 通用函数标记模板:绑定函数模板的身份 template<template<int...> class F> struct function_tag_t {};
- 针对你定义的
getter函数模板,生成对应的全局标记常量(不需要修改原getter的任何实现)
// 保留原getter的两个递归重载定义完全不变 inline constexpr auto getter_tag = function_tag_t<getter>{};
- 调整
index_filter特化版本的execute实现,替换原来错误的模板模板参数逻辑
template<int... rests> struct index_filter<1, rests...> { // 接收function_tag类型的标记,以及后续的调用参数 template<template<int...> class F, class... Args> inline static void execute(function_tag_t<F>, Args&&... args) { // 核心:将内部生成的rests...作为显式模板参数实例化目标函数,再转发参数 F<rests...>(std::forward<Args>(args)...); } };
此时可以直接实现预期的调用形式:
poker_index out; pokers_rep rep; // 传入getter对应的标记对象即可完成调用 index_filter<4>::execute(getter_tag, rep, out);
如果希望完全和原调用写法一致,可以用宏把标记常量直接命名为getter,消除名字差异。
方案2:C++20泛型Lambda包装(更简洁的写法)
如果你使用的是支持C++20的编译器,可以用显式模板参数的泛型Lambda直接包装getter,不需要额外写标记模板:
- 给原
getter函数搭配一个泛型Lambda常量:
inline constexpr auto getter = []<int... Idx>(pokers_rep& rep, poker_index& out) { // 直接转发到原getter的递归实现,不改变原有逻辑 return ::getter<Idx...>(rep, out); };
- 对应的
execute实现调整为接收泛型可调用对象:
template<int... rests> struct index_filter<1, rests...> { template<class Fn, class... Args> inline static void execute(Fn&& fn, Args&&... args) { // 把内部的rests...作为显式模板参数传给Lambda的operator() fn.template operator()<rests...>(std::forward<Args>(args)...); } };
这种写法下,调用形式和你最初的预期完全一致:
poker_index out; index_filter<4>::execute(getter, rep, out);
复用说明
两种方案都完全支持你最初的复用目标:所有和getter形式类似的函数(即拥有int...非类型模板参数、相同参数列表的业务函数),只需要用相同的方式生成对应的标记对象/包装Lambda,就可以直接传入index_filter::execute,不需要修改index_filter本身的实现。
补充修正:原
index_filter的索引生成逻辑当前会生成从1到N-1的参数包,如果需要包含0起始的索引,只需要调整递归继承的参数即可,不影响函数传递的核心逻辑。
内容的提问来源于stack exchange,提问作者Keanyuan
相关产品推荐
相关产品推荐

