C++模板与OOD困境:如何实现类的模板化成员变量?
C++类型专属注册表的非静态实现方案
你想要实现一个实例级的Registries类,为每种类型自动维护独立的vector注册表,避免静态内存占用和重复代码。以下是可行的替代方案:
先明确核心需求与现有问题
你期望的理想逻辑(虽不合法)是为每个Registries实例自动创建对应类型的vector:
class Registries { public: template<typename T> void AddToRegistry(T thing) { registry.push_back(thing); } template<typename T> std::vector<T>* GetRegistry<T>() { return ®istry(); } private: template<typename T> std::vector<T> registry; // C++不支持类模板成员变量 };
问题在于C++不允许类拥有模板化的成员变量,无法为每种类型自动生成vector实例。
你尝试过的两个方案都有明显缺陷:
- 静态成员注册表:所有
Registries实例共享同一静态vector,无法实现实例级隔离 - switch/if类型判断:模板函数实例化时会出现类型不匹配的编译错误,因为
cool_registry.push_back(thing)在T不是CoolThing时无法通过编译
方案1:动态类型注册表(C++17+)
此方案支持动态添加任意类型的注册表,每个Registries实例拥有独立的存储空间,无需提前声明所有类型:
#include <vector> #include <any> #include <typeindex> #include <unordered_map> class Registries { public: template<typename T> void AddToRegistry(T thing) { auto& vec = GetOrCreateVector<T>(); vec.push_back(std::move(thing)); } template<typename T> std::vector<T>* GetRegistry() { auto it = registries.find(std::type_index(typeid(T))); return (it != registries.end()) ? std::any_cast<std::vector<T>*>(it->second) : nullptr; } // 清空指定类型的注册表 template<typename T> void ClearRegistry() { registries.erase(std::type_index(typeid(T))); } // 清空所有注册表 void ClearAll() { registries.clear(); } private: template<typename T> std::vector<T>& GetOrCreateVector() { const auto key = std::type_index(typeid(T)); auto it = registries.find(key); if (it == registries.end()) { auto vec_ptr = new std::vector<T>(); registries[key] = vec_ptr; return *vec_ptr; } return *std::any_cast<std::vector<T>*>(it->second); } std::unordered_map<std::type_index, std::any> registries; // 析构时清理所有动态分配的vector ~Registries() { for (auto& [_, ptr] : registries) { std::any_cast<void(*)(void*)>( [](void* p) { delete static_cast<std::vector<void>*>(p); } )(std::any_cast<void*>(ptr)); } } // 禁用拷贝,避免内存管理问题 Registries(const Registries&) = delete; Registries& operator=(const Registries&) = delete; }; // 测试类型 struct CoolThing {}; struct NiceThing {}; int main() { Registries registries; CoolThing thing1; NiceThing thing2; registries.AddToRegistry(thing1); registries.AddToRegistry(thing2); std::vector<CoolThing>* cool_reg = registries.GetRegistry<CoolThing>(); std::vector<NiceThing>* nice_reg = registries.GetRegistry<NiceThing>(); if (cool_reg) { // 使用cool_reg... } }
核心原理:
- 用
std::type_index作为每种类型的唯一标识,将类型映射到对应的std::vector<T>指针 std::any实现类型擦除,允许不同类型的vector指针存储在同一个unordered_map中GetOrCreateVector<T>负责查找或创建对应类型的vector,保证每个类型在实例中唯一- 析构函数自动清理所有动态分配的内存,避免泄漏
方案2:编译期确定的注册表(C++17+)
如果可以提前明确所有需要注册的类型,此方案无需动态内存分配,性能更优:
#include <vector> #include <tuple> #include <type_traits> // 辅助模板:从元组中提取对应类型的vector template<typename T, typename Tuple> struct VectorGetter; template<typename T, typename... Ts> struct VectorGetter<T, std::tuple<std::vector<Ts>...>> { static_assert((std::is_same_v<T, Ts> || ...), "Type not supported by registry"); static std::vector<T>& get(std::tuple<std::vector<Ts>...>& tuple) { return std::get<std::vector<T>>(tuple); } }; class Registries { public: // 声明支持的所有注册类型,可按需扩展 using SupportedTypes = std::tuple<std::vector<CoolThing>, std::vector<NiceThing>>; template<typename T> void AddToRegistry(T thing) { auto& vec = VectorGetter<T, SupportedTypes>::get(registries_); vec.push_back(std::move(thing)); } template<typename T> std::vector<T>* GetRegistry() { try { return &VectorGetter<T, SupportedTypes>::get(registries_); } catch (const std::bad_variant_access&) { return nullptr; } } private: SupportedTypes registries_; }; // 测试类型 struct CoolThing {}; struct NiceThing {}; int main() { Registries registries; CoolThing thing1; NiceThing thing2; registries.AddToRegistry(thing1); registries.AddToRegistry(thing2); auto cool_reg = registries.GetRegistry<CoolThing>(); auto nice_reg = registries.GetRegistry<NiceThing>(); }
核心原理:
- 用
std::tuple存储所有类型的vector,编译期确定所有注册表结构 - 辅助模板
VectorGetter在编译期定位元组中对应类型的vector - 无动态内存分配,所有注册表都是
Registries实例的成员变量,完全实现实例级隔离
内容的提问来源于stack exchange,提问作者InquiryOrEnquiry
相关产品推荐
相关产品推荐

