带模板参数的构造器:Dynamic类的类型指定构造方案问询
好的,针对你的需求——在C++11下实现一个能存储多种类型的Dynamic类,支持指定类型并就地构造对象(不能用RTTI),我提供两种可行的方案,你可以根据自己的场景选择:
方案一:基于动态分配+类型标签(灵活,适合大量类型)
这种方案用void*存储对象的动态分配内存,配合自实现的类型标签来区分不同类型,不需要为每个类型写大量特化代码,非常适合你提到的50种类型的场景。
步骤1:实现类型标签系统
首先我们需要一个能给每个类型分配唯一ID的机制,替代RTTI的typeid:
#include <utility> #include <stdexcept> #include <vector> // 为每个类型生成唯一ID的模板 template <typename T> struct TypeId { static const int value; }; // 全局计数器,确保每个类型ID唯一 namespace { int type_id_counter = 0; } template <typename T> const int TypeId<T>::value = type_id_counter++;
步骤2:实现Dynamic类
class Dynamic { private: void* m_data = nullptr; int m_type_id = -1; // 存储析构函数指针,用于正确销毁不同类型的对象 void (*m_dtor)(void*) = nullptr; public: // 模板构造器:显式指定要构造的类型,转发构造参数 template <typename T, typename... Args> explicit Dynamic(Args&&... args) : m_type_id(TypeId<T>::value) { // 就地构造T对象,完美转发参数避免不必要的拷贝 m_data = new T(std::forward<Args>(args)...); // 存储对应类型的析构逻辑 m_dtor = [](void* ptr) { delete static_cast<T*>(ptr); }; } // 析构函数:调用存储的析构函数销毁对象 ~Dynamic() { if (m_dtor) { m_dtor(m_data); } } // 禁用拷贝语义(如果需要拷贝,需额外实现类型安全的拷贝逻辑) Dynamic(const Dynamic&) = delete; Dynamic& operator=(const Dynamic&) = delete; // 移动语义:转移资源所有权 Dynamic(Dynamic&& other) noexcept : m_data(other.m_data), m_type_id(other.m_type_id), m_dtor(other.m_dtor) { other.m_data = nullptr; other.m_type_id = -1; other.m_dtor = nullptr; } Dynamic& operator=(Dynamic&& other) noexcept { if (this != &other) { if (m_dtor) { m_dtor(m_data); } m_data = other.m_data; m_type_id = other.m_type_id; m_dtor = other.m_dtor; other.m_data = nullptr; other.m_type_id = -1; other.m_dtor = nullptr; } return *this; } // 获取存储的对象:类型安全的访问 template <typename T> T& get() { // 编译期检查类型是否被支持(只要用过该类型构造Dynamic,就会有ID) static_assert(TypeId<T>::value != -1, "Type not supported by Dynamic"); // 运行期检查类型匹配 if (m_type_id != TypeId<T>::value) { throw std::invalid_argument("Type mismatch in Dynamic::get()"); } return *static_cast<T*>(m_data); } template <typename T> const T& get() const { static_assert(TypeId<T>::value != -1, "Type not supported by Dynamic"); if (m_type_id != TypeId<T>::value) { throw std::invalid_argument("Type mismatch in Dynamic::get()"); } return *static_cast<const T*>(m_data); } };
使用示例
// 构造一个存储std::vector<double>的Dynamic对象,就地创建长度为10的vector Dynamic vec_dyn = Dynamic<std::vector<double>>(10); // 获取并使用内部的vector auto& vec = vec_dyn.get<std::vector<double>>(); vec.push_back(3.14); // 构造存储int的对象 Dynamic int_dyn = Dynamic<int>(42); std::cout << int_dyn.get<int>() << std::endl;
方案二:基于Union+手动类型管理(性能更好,适合类型数量固定)
如果你的50种类型是固定的,并且希望避免动态分配的开销,可以用C++11支持的非POD类型union,手动管理构造和析构:
步骤1:定义类型枚举和Union
#include <utility> #include <stdexcept> #include <vector> class Dynamic { private: // 枚举所有支持的类型 enum class Type { Int, Double, VectorInt, VectorDouble, // ... 这里添加剩下的46种类型 ... }; Type m_type; // 存储所有支持类型的union(C++11允许非POD类型) union Storage { int int_val; double double_val; std::vector<int> vec_int; std::vector<double> vec_double; // ... 这里添加剩下的46种类型 ... // 构造函数:默认构造(union的成员默认不初始化) Storage() {} // 析构函数:空,因为我们手动管理析构 ~Storage() {} } m_storage; // 模板特化的构造函数,针对每个类型 template <typename T, typename... Args> void construct(Args&&... args); // 模板特化的析构函数,针对每个类型 template <typename T> void destroy(); public: // 模板构造器:指定类型并转发参数 template <typename T, typename... Args> explicit Dynamic(Args&&... args) { construct<T>(std::forward<Args>(args)...); } ~Dynamic() { // 根据当前类型调用对应的析构逻辑 switch(m_type) { case Type::Int: break; // POD类型无需析构 case Type::Double: break; case Type::VectorInt: destroy<std::vector<int>>(); break; case Type::VectorDouble: destroy<std::vector<double>>(); break; // ... 这里添加剩下的46种类型的析构分支 ... } } // 禁用拷贝,如需拷贝需手动实现每个类型的拷贝逻辑 Dynamic(const Dynamic&) = delete; Dynamic& operator=(const Dynamic&) = delete; // 移动语义 Dynamic(Dynamic&& other) noexcept : m_type(other.m_type) { // 针对每个类型移动资源 switch(m_type) { case Type::Int: m_storage.int_val = other.m_storage.int_val; break; case Type::Double: m_storage.double_val = other.m_storage.double_val; break; case Type::VectorInt: new (&m_storage.vec_int) std::vector<int>(std::move(other.m_storage.vec_int)); other.m_storage.vec_int.~vector(); break; case Type::VectorDouble: new (&m_storage.vec_double) std::vector<double>(std::move(other.m_storage.vec_double)); other.m_storage.vec_double.~vector(); break; // ... 其他类型的移动逻辑 ... } other.m_type = static_cast<Type>(-1); // 标记为无效 } // 获取存储的对象 template <typename T> T& get(); template <typename T> const T& get() const; }; // 实现construct的特化 template <> void Dynamic::construct<int>(int val) { m_type = Type::Int; m_storage.int_val = val; } template <> void Dynamic::construct<double>(double val) { m_type = Type::Double; m_storage.double_val = val; } template <typename... Args> void Dynamic::construct<std::vector<int>>(Args&&... args) { m_type = Type::VectorInt; new (&m_storage.vec_int) std::vector<int>(std::forward<Args>(args)...); } template <typename... Args> void Dynamic::construct<std::vector<double>>(Args&&... args) { m_type = Type::VectorDouble; new (&m_storage.vec_double) std::vector<double>(std::forward<Args>(args)...); } // 实现destroy的特化 template <> void Dynamic::destroy<std::vector<int>>() { m_storage.vec_int.~vector(); } template <> void Dynamic::destroy<std::vector<double>>() { m_storage.vec_double.~vector(); } // 实现get的特化 template <> int& Dynamic::get<int>() { if (m_type != Type::Int) throw std::invalid_argument("Type mismatch"); return m_storage.int_val; } template <> const int& Dynamic::get<int>() const { if (m_type != Type::Int) throw std::invalid_argument("Type mismatch"); return m_storage.int_val; } template <> std::vector<int>& Dynamic::get<std::vector<int>>() { if (m_type != Type::VectorInt) throw std::invalid_argument("Type mismatch"); return m_storage.vec_int; } // ... 其他类型的get特化 ...
使用示例
// 就地构造长度为10的std::vector<double> Dynamic vec_dyn = Dynamic<std::vector<double>>(10); auto& vec = vec_dyn.get<std::vector<double>>(); vec[0] = 2.718; Dynamic int_dyn = Dynamic<int>(100); std::cout << int_dyn.get<int>() << std::endl;
方案对比
- 方案一:优点是灵活,新增类型不需要修改Dynamic类的核心代码,只需要用该类型构造一次Dynamic即可;缺点是需要动态分配内存,有轻微的性能开销。
- 方案二:优点是没有动态分配,性能更高;缺点是新增类型需要修改枚举、union、construct/destroy/get的特化代码,维护成本较高,适合类型固定的场景。
内容的提问来源于stack exchange,提问作者InsideLoop
相关产品推荐
相关产品推荐

