如何在 C++ 中存储变体数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/208959/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:43:57  来源:igfitidea点击:

How to store variant data in C++

c++variant

提问by Perculator

I'm in the process of creating a class that stores metadata about a particular data source. The metadata is structured in a tree, very similar to how XML is structured. The metadata values can be integer, decimal, or string values.

我正在创建一个类来存储有关特定数据源的元数据。元数据以树的形式构建,与 XML 的结构非常相似。元数据值可以是整数、十进制或字符串值。

I'm curious if there is a good way in C++ to store variant data for a situation like this. I'd like for the variant to use standard libraries, so I'm avoiding the COM, Ole, and SQL VARIANT types that are available.

我很好奇在 C++ 中是否有一种好方法可以为这种情况存储变体数据。我希望变体使用标准库,因此我避免使用可用的 COM、Ole 和 SQL VARIANT 类型。

My current solution looks something like this:

我当前的解决方案如下所示:

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;
};

The MetaValue class has various Get functions for obtaining the currently stored variant value, but it ends up making every query for a value a big block of if/else if statements to figure out which value I'm looking for.

MetaValue 类具有用于获取当前存储的变体值的各种 Get 函数,但它最终使每个对值的查询成为一大块 if/else if 语句,以找出我正在寻找的值。

I've also explored storing the value as only a string, and performing conversions to get different variant types out, but as far as I've seen this leads to a bunch of internal string parsing and error handling which isn't pretty, opens up a big old can of precision and data loss issues with floating point values, and still doesn't eliminate the query if/else if issue stated above.

我还探索了将值仅存储为字符串,并执行转换以获取不同的变体类型,但据我所知,这会导致一堆内部字符串解析和错误处理,这并不漂亮,打开使用浮点值解决了一大堆精度和数据丢失问题,并且仍然没有消除查询 if/else if 上面提到的问题。

Has anybody implemented or seen something that's cleaner to use for a C++ variant data type using standard libraries?

有没有人使用标准库实现或看到更清晰的用于 C++ 变体数据类型的东西?

回答by Konrad Rudolph

As of C++17, there's std::variant.

从 C++17 开始,有std::variant.

If you can't use that yet, you might want Boost.Variant. A similar, but distinct, type for modelling polymorphism is provided by std::any(and, pre-C++17, Boost.Any).

如果您还不能使用它,您可能需要Boost.Variant。类似但不同的多态建模类型由std::any(和 C++17 之前的Boost.Any 提供)。

Just as an additional pointer, you can look for “type erasure”.

就像一个额外的指针,你可以寻找“类型擦除”。

回答by Fox

While Konrad's answer (using an existing standardized solution) is certainly preferable to writing your own bug-prone version, the boost variant has some overheads, especially in copy construction and memory.

虽然 Konrad 的答案(使用现有的标准化解决方案)肯定比编写自己的容易出错的版本更可取,但 boost 变体有一些开销,尤其是在复制构造和内存方面。

A common customized approach is the following modified Factory Pattern:

一种常见的定制方法是以下修改后的工厂模式:

  1. Create a Base interface for a generic object that also encapsulates the object type (either as an enum), or using 'typeid' (preferable).
  2. Now implement the interface using a template Derivedclass.
  3. Create a factory class with a templateized createfunction with signature:
  1. 为也封装对象类型(作为枚举)或使用“typeid”(首选)的通用对象创建一个 Base 接口。
  2. 现在使用模板Derived类实现接口。
  3. 创建一个create带有签名的模板化函数的工厂类:

template <typename _T> Base * Factory::create ();

template <typename _T> Base * Factory::create ();

This internally creates a Derived<_T>object on the heap, and retuns a dynamic cast pointer. Specialize this for each class you want implemented.

这在内部Derived<_T>在堆上创建一个对象,并返回一个动态转换指针。为您想要实现的每个类专门化这个。

Finally, define a Variantwrapper that contains this Base *pointer and defines template get and set functions. Utility functions like getType(), isEmpty(), assignment and equality operators, etc can be appropriately implemented here.

最后,定义一个Variant包含此Base *指针的包装器并定义模板 get 和 set 函数。可以在此处适当地实现诸如getType(), isEmpty(),赋值和相等运算符等实用功能。

Depending on the utility functions and the factory implementation, supported classes will need to support some basic functions like assignment or copy construction.

根据实用程序函数和工厂实现,受支持的类需要支持一些基本函数,如赋值或复制构造。

回答by Paul Nathan

You can also go down to a more C-ish solution, which would have a void* the size of a double on your system, plus an enum for which type you're using. It's reasonably clean, but definitely a solution for someone who feels wholly comfortable with the raw bytes of the system.

您还可以使用更 C 风格的解决方案,该解决方案在您的系统上会有一个双精度大小的 void*,以及您正在使用的类型的枚举。它相当干净,但对于那些对系统的原始字节感到完全满意的人来说绝对是一个解决方案。

回答by Matt Klein

C++17 now has std::variantwhich is exactly what you're looking for.

C ++ 17 现在拥有std::variant这正是您正在寻找的。

std::variant

标准::变体

回答by Gábor Angyal

Although the question had been answered for a long time, for the record I would like to mention that QVariantalso does this.

虽然这个问题已经回答了很长时间,但为了记录,我想提一下QVariant也这样做。