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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:57:39