如何存储构造函数传入的可变模板参数并实现类tuple的Storage类
实现动态参数存储类Storage
要实现一个无需提前指定类型、能存储任意参数包且可放入统一容器的类,核心思路是复用std::tuple做内部存储,配合类型擦除基类实现容器兼容性,具体实现如下:
1. 定义类型擦除基类
为了让不同参数类型的Storage实例能存入同一个容器,需要一个非模板基类:
#include <tuple> #include <vector> #include <memory> #include <stdexcept> #include <iostream> #include <string> // 非模板基类,用于实现类型擦除 class StorageBase { public: virtual ~StorageBase() = default; // 虚析构确保子类资源正确释放 };
2. 实现Storage模板类
继承基类,用std::tuple存储参数包,同时提供类似std::get的索引访问方法:
template<typename... Args> class Storage : public StorageBase { private: std::tuple<Args...> data; // 内部用tuple存储参数 public: // 构造函数:完美转发参数包到tuple Storage(Args&&... args) : data(std::forward<Args>(args)...) {} // 按索引获取可变引用元素 template<std::size_t Index> auto& get() { static_assert(Index < sizeof...(Args), "索引超出参数包范围"); return std::get<Index>(data); } // 按索引获取const引用元素 template<std::size_t Index> const auto& get() const { static_assert(Index < sizeof...(Args), "索引超出参数包范围"); return std::get<Index>(data); } // 获取参数包的元素数量 static constexpr std::size_t size() { return sizeof...(Args); } };
3. 使用示例
演示如何创建实例、存入统一容器,以及访问元素:
int main() { // 创建不同参数类型的Storage实例 auto storage_int_str = std::make_unique<Storage<int, std::string>>(5, "hello world"); auto storage_double_bool = std::make_unique<Storage<double, bool>>(3.14, true); // 将不同实例存入同一个vector(存储基类智能指针) std::vector<std::unique_ptr<StorageBase>> storage_list; storage_list.push_back(std::move(storage_int_str)); storage_list.push_back(std::move(storage_double_bool)); // 访问第一个实例的元素:需动态转型到具体类型 if (auto* s = dynamic_cast<Storage<int, std::string>*>(storage_list[0].get())) { std::cout << "第一个元素:" << s->get<0>() << ", " << s->get<1>() << "\n"; } // 访问第二个实例的元素 if (auto* s = dynamic_cast<Storage<double, bool>*>(storage_list[1].get())) { std::cout << "第二个元素:" << s->get<0>() << ", " << std::boolalpha << s->get<1>() << "\n"; } return 0; }
关键说明
- 内部存储:直接用
std::tuple处理参数包的类型推导和存储,无需手动实现复杂的类型存储逻辑。 - 容器兼容性:通过非模板基类
StorageBase实现类型擦除,让不同模板实例的Storage能存入同一个容器(需用指针/智能指针存储,避免对象切片)。 - 元素访问:
get方法通过模板索引配合std::get实现,静态断言能在编译期检查索引合法性;从容器取出元素时需用dynamic_cast转型到具体类型,这是类型擦除的必然代价——运行时必须明确存储的具体类型才能调用对应版本的get。 - 性能优化:构造函数使用完美转发
std::forward,减少不必要的拷贝操作。
内容的提问来源于stack exchange,提问作者TwistedBlizzard
相关产品推荐
相关产品推荐

