C++中如何动态创建对象以属性形式获取数据?求更优方案
灵活属性存储的优化实现方案
针对你需要存储数据库动态属性的需求,以下几种方案可以解决std::variant手动指定类型的痛点,同时保留灵活性:
1. 封装自动推导类型的属性获取函数
基于你当前的std::map+variant方案,封装模板函数让编译器自动推导目标类型,省去手动写std::get<T>的模板参数:
#include <map> #include <variant> #include <stdexcept> #include <string> // 定义属性类型别名 using AttrMap = std::map<std::string, std::variant<int32_t, float, std::string>>; // 基础获取函数,不存在或类型不匹配时抛出异常 template<typename T> T get_attr(const AttrMap& attr_map, const std::string& key) { auto it = attr_map.find(key); if (it == attr_map.end()) { throw std::invalid_argument("Attribute not found: " + key); } return std::get<T>(it->second); } // 带默认值的重载,避免异常 template<typename T> T get_attr(const AttrMap& attr_map, const std::string& key, const T& default_val) { auto it = attr_map.find(key); if (it == attr_map.end()) { return default_val; } if (auto val_ptr = std::get_if<T>(&it->second)) { return *val_ptr; } return default_val; }
调用时无需手动指定类型,编译器会根据接收变量的类型自动推导:
// 自动推导T为int32_t int32_t user_id = get_attr(attr_map, "user_id"); // 自动推导T为float float score = get_attr(attr_map, "score"); // 带默认值的调用 std::string username = get_attr(attr_map, "username", std::string("unknown"));
2. 使用泛型访问器自动匹配类型
如果需要对属性做类型分支处理,用std::visit结合泛型lambda,完全无需手动指定类型,编译期自动匹配variant的实际类型:
#include <iostream> void process_attribute(const std::variant<int32_t, float, std::string>& attr) { std::visit([](auto&& val) { using ValType = std::decay_t<decltype(val)>; if constexpr (std::is_same_v<ValType, int32_t>) { std::cout << "Integer value: " << val << "\n"; } else if constexpr (std::is_same_v<ValType, float>) { std::cout << "Float value: " << val << "\n"; } else if constexpr (std::is_same_v<ValType, std::string>) { std::cout << "String value: " << val << "\n"; } }, attr); } // 调用示例 process_attribute(attr_map["score"]);
这种方式既保留了类型安全,又省去了手动判断类型的冗余代码。
3. 自定义动态属性容器(支持任意类型)
如果需要支持更多类型且不想修改variant的模板参数列表,可以自己实现一个基于类型擦除的动态容器:
#include <map> #include <typeinfo> #include <memory> #include <stdexcept> #include <string> class AttributeBase { public: virtual ~AttributeBase() = default; virtual const std::type_info& get_type() const = 0; }; template<typename T> class AttributeValue : public AttributeBase { public: AttributeValue(T val) : value_(std::move(val)) {} const std::type_info& get_type() const override { return typeid(T); } const T& get() const { return value_; } T& get() { return value_; } private: T value_; }; class DynamicAttrStore { public: template<typename T> void set(const std::string& key, T value) { store_[key] = std::make_unique<AttributeValue<T>>(std::move(value)); } template<typename T> const T& get(const std::string& key) const { auto it = store_.find(key); if (it == store_.end()) { throw std::invalid_argument("Attribute not found: " + key); } if (it->second->get_type() != typeid(T)) { throw std::invalid_argument("Attribute type mismatch"); } return static_cast<AttributeValue<T>*>(it->second.get())->get(); } private: std::map<std::string, std::unique_ptr<AttributeBase>> store_; };
使用时可以自由添加任意类型的属性,获取时只需指定目标类型(编译器也可在部分场景推导):
DynamicAttrStore store; store.set("user_id", int32_t(1001)); store.set("score", 95.5f); store.set("is_vip", true); // 新增bool类型无需修改容器定义 int32_t id = store.get<int32_t>("user_id"); bool is_vip = store.get<bool>("is_vip");
4. C++20概念增强类型安全
如果使用C++20及以上版本,可以用概念(Concepts)约束允许的属性类型,在编译期拦截非法类型:
#include <concepts> template<typename T> concept ValidAttrType = std::is_same_v<T, int32_t> || std::is_same_v<T, float> || std::is_same_v<T, std::string>; // 仅允许符合ValidAttrType的类型调用 template<ValidAttrType T> T get_attr(const AttrMap& attr_map, const std::string& key) { auto it = attr_map.find(key); if (it == attr_map.end()) { throw std::invalid_argument("Attribute not found: " + key); } return std::get<T>(it->second); }
这样如果尝试获取不支持的类型,编译阶段就会报错,避免运行时异常。
内容的提问来源于stack exchange,提问作者Артур Жовтобрюх
相关产品推荐
相关产品推荐

