不修改Runner模板类前提下,能否遍历模板类列表?
首先明确你的场景:现有不可修改的Runner模板类,需要遍历一组模板类(A、B、C),逐个调用runner.dosomething<X>()。
你的现有代码
Runner类定义
template<typename T> struct Runner { template<template<typename> typename U> auto dosomething() { U<T> u; // 用模板参数T实例化传入的模板类U } };
手动调用示例
template<typename T> struct A {}; template<typename T> struct B {}; template<typename T> struct C {}; int main() { Runner<int> runner; runner.dosomething<A>(); runner.dosomething<B>(); runner.dosomething<C>(); }
需求与遇到的问题
你希望将A、B、C封装成模板类列表,通过遍历实现批量调用,伪代码如下:
// 伪代码(C++中无法直接运行) using typelist = magictypelist<A,B,C>; typelist::iterate ([&] <typename X> { runner.dosomething<X>(); });
尝试过用std::tuple存储实例化后的类型,但无法从A<T>这类具体类型中还原出原始模板类A,因此无法匹配dosomething所需的模板类参数。
解决方案:自定义模板类列表容器+编译期遍历
我们可以通过模板元编程定义专门存储模板类的容器,再配合编译期遍历实现需求,完全不需要修改Runner类。
1. 定义模板类列表容器
这个容器直接存储模板类模板,而非具体类型:
// 模板类列表容器,接受任意数量的单参数模板类 template<template<typename> typename... Ts> struct TemplateList {};
用TemplateList<A, B, C>即可表示你的模板类列表。
2. 实现遍历调用逻辑
根据C++版本选择不同实现方式:
方式一:C++17及以上(折叠表达式,简洁高效)
// 通用遍历函数,支持任意类型的Runner实例 template<typename RunnerInstance, template<typename> typename... Ts> void iterate_template_list(RunnerInstance& runner, TemplateList<Ts...>) { // 折叠表达式展开,逐个调用dosomething (runner.template dosomething<Ts>(), ...); }
注意:
runner.template dosomething<Ts>()中的template关键字是必须的——因为RunnerInstance是模板参数,编译器无法自动推断dosomething是模板函数,需要显式指明。
方式二:C11/C14兼容(递归模板特化)
// 递归终止条件:空列表 template<typename RunnerInstance> void iterate_template_list(RunnerInstance&, TemplateList<>) {} // 递归展开:处理第一个模板类,再递归处理剩余列表 template<typename RunnerInstance, template<typename> typename First, template<typename> typename... Rest> void iterate_template_list(RunnerInstance& runner, TemplateList<First, Rest...>) { runner.template dosomething<First>(); iterate_template_list(runner, TemplateList<Rest...>()); }
3. 使用示例
int main() { Runner<int> runner; // 定义你的模板类列表 using MyTemplateList = TemplateList<A, B, C>; // 遍历调用 iterate_template_list(runner, MyTemplateList{}); // 也支持其他类型的Runner Runner<double> d_runner; iterate_template_list(d_runner, MyTemplateList{}); }
运行这段代码后,会自动依次调用runner.dosomething<A>()、runner.dosomething<B>()、runner.dosomething<C>(),完全符合你的需求。
原理说明
TemplateList直接存储模板类模板(比如A),而dosomething需要的参数正好是template<typename> typename U类型的模板类,因此可以直接传递。相比用std::tuple存储具体类型的方案,这种方式不需要从实例化后的类型中反向提取模板类,逻辑更直接,也完全适配Runner的API。
内容的提问来源于stack exchange,提问作者abcdefg

