C++如何遍历可变模板类型(而非实参)?
解决可变模板参数类型的遍历订阅问题
这个问题属于C可变模板参数展开的典型场景,根据你使用的C版本,有几种简洁的实现方式,我来给你一步步拆解:
一、C++17及以上:用折叠表达式实现(最简洁)
C++17引入的折叠表达式是处理可变参数包最直观的方式,直接在构造函数里就能完成遍历:
#include <iostream> #include <typeinfo> // 假设PubSub是你的发布订阅对象,这里模拟一个示例实现 struct PubSub { template<typename X> static void Subscribe(auto callback) { // C++20支持auto参数,C++14可以用std::function std::cout << "已订阅类型: " << typeid(X).name() << "\n"; // 实际逻辑:将callback存储到对应类型的订阅列表中 } }; template<typename... T> class Subscriber { public: Subscriber() { // 折叠表达式:对每个T类型执行Subscribe调用 (PubSub.Subscribe<T>([](auto data) { // 这里处理收到的数据,示例打印类型和内容 process_data<T>(data); }), ...); } private: // 可选:辅助函数,方便针对不同类型做差异化处理 template<typename X> void process_data(const X& data) { std::cout << "收到数据[" << typeid(X).name() << "]: " << data << "\n"; } };
代码解释:
(expr, ...)是逗号折叠表达式,会把参数包T...中的每个类型依次代入expr执行,逗号运算符保证每个Subscribe调用都被顺序执行。- lambda里的
auto data在C14及以上支持,如果是C11,需要把回调改成std::function<void(const X&)>并指定参数类型。
二、C11/C14兼容方案:初始化列表展开
如果你的项目还在使用C++17之前的标准,可以利用数组初始化列表的技巧来展开参数包:
#include <iostream> #include <typeinfo> #include <functional> struct PubSub { template<typename X> static void Subscribe(std::function<void(const X&)> callback) { std::cout << "已订阅类型: " << typeid(X).name() << "\n"; } }; template<typename... T> class Subscriber { public: Subscriber() { // 利用dummy数组的初始化过程展开参数包 using dummy = int[]; (void)dummy{0, ( PubSub.Subscribe<T>([](const T& data) { process_data<T>(data); }), 0)...}; } private: template<typename X> void process_data(const X& data) { std::cout << "收到数据[" << typeid(X).name() << "]: " << data << "\n"; } };
代码解释:
- 创建一个临时的
dummy整数数组,用0作为占位符,每个参数包元素对应一个(Subscribe调用, 0)的表达式。 - 数组初始化时会依次处理每个元素,从而触发每个
Subscribe调用,达到遍历参数包的目的。 (void)用来避免编译器对未使用数组的警告。
三、使用示例
不管用哪种方案,你都可以这样实例化Subscriber类:
int main() { // 订阅int、std::string、double三种类型 Subscriber<int, std::string, double> sub; return 0; }
运行后会输出类似(类型名可能因编译器不同而略有差异):
已订阅类型: int 已订阅类型: std::basic_string<char, std::char_traits<char>, std::allocator<char> > 已订阅类型: double
内容的提问来源于stack exchange,提问作者Cashif Ilyas
相关产品推荐
相关产品推荐

