题
我正在创建一个存储有关特定数据源的元数据的类。元数据采用树状结构,与 XML 的结构非常相似。元数据值可以是整数、小数或字符串值。
我很好奇 C++ 中是否有一种好方法来存储这种情况的变体数据。我希望变体使用标准库,因此我避免使用可用的 COM、Ole 和 SQL VARIANT 类型。
我当前的解决方案看起来像这样:
enum MetaValueType
{
MetaChar,
MetaString,
MetaShort,
MetaInt,
MetaFloat,
MetaDouble
};
union MetaUnion
{
char cValue;
short sValue;
int iValue;
float fValue;
double dValue;
};
class MetaValue
{
...
private:
MetaValueType ValueType;
std::string StringValue;
MetaUnion VariantValue;
};
MetaValue 类具有各种 Get 函数,用于获取当前存储的变体值,但它最终会使每个值的查询成为一大块 if/else if 语句,以找出我正在查找的值。
我还探索了将值仅存储为字符串,并执行转换以获取不同的变体类型,但据我所知,这会导致一堆内部字符串解析和错误处理,这并不漂亮,打开解决了浮点值的一大堆精度和数据丢失问题,并且仍然不能消除上述查询 if/else if 问题。
有没有人使用标准库实现或看到过更干净的用于 C++ 变体数据类型的东西?
解决方案
从C ++ 17开始,有 std: :变体代码>
如果你还没有使用它,你可能想要 Boost.Variant 。用于建模多态性的类似但不同的类型由 std :: any提供。
(以及,预C ++ 17, Boost.Any )。
作为附加指针,您可以查找“ 键入擦除”。
其他提示
虽然Konrad的答案(使用现有的标准化解决方案)肯定比编写自己的易受错误的版本更好,但是boost变体有一些开销,特别是在复制构造和内存中。
常见的自定义方法是以下修改后的工厂模式:
- 为通用对象创建一个Base接口,该接口也封装对象类型(作为枚举)或使用'typeid'(最好)。
- 现在使用模板
Derived
类实现接口。 - 使用带签名的模板化
create
函数创建工厂类:
醇>
template< typename _T> Base * Factory :: create();
这会在堆内部创建 Derived< _T>
对象,并返回动态转换指针。专门针对您想要实施的每个课程。
最后,定义一个包含此 Base *
指针的 Variant
包装器,并定义模板get和set函数。像 getType()
, isEmpty()
,赋值和相等运算符等实用函数可以在这里适当地实现。
根据实用程序功能和工厂实现,受支持的类需要支持一些基本功能,如赋值或复制构造。
您还可以使用更多C-ish解决方案,该系统将具有系统上双倍大小的void *,以及您正在使用的类型的枚举。它相当干净,但对于那些对系统的原始字节感到完全满意的人来说绝对是一种解决方案。
C++17 现在有 std::variant
这正是您正在寻找的。
虽然这个问题已经回答了很长时间,但为了记录,我想提一下 QVariant 也是这样做的。