简体   繁体   English

Class 中联合的默认初始化

[英]Default Initialization of a Union within a Class

I'm in the process of updating some legacy c++ code from Visual Studio 2013 to VS2019 and came across an interesting problem:我正在将一些旧版 c++ 代码从 Visual Studio 2013 更新到 VS2019,并遇到了一个有趣的问题:

The code in question uses a class called Attrib.h to represent any possible unit of data in a table's cell.有问题的代码使用名为 Attrib.h 的 class 来表示表格单元格中任何可能的数据单元。 This data could be a bool, int, double, Guid/Uuid, various types of geometry data, text, etc. As such, Attrib.h includes a Union of many different structs used to represent these different possible data types.该数据可以是 bool、int、double、Guid/Uuid、各种类型的几何数据、文本等。因此,Attrib.h 包含许多不同结构的联合,用于表示这些不同的可能数据类型。 I've included an abridged version below:我在下面包含了一个精简版:

class Attrib
{

public:

union
{
    double  vDouble;
    bool    vBool;

    struct sUnion
    {
#ifdef _WIN64
            BYTE Data[24];
#else
            BYTE Data[16];
#endif
    } vUnion;

    union
    {
        struct
        {
            INT32   vInt32;
            INT32   vInt32High; 
        };
        INT64       vInt64;
    };
    struct sStr
    {
        //etc
    } vStr;

    struct sBin
    {
        //etc
    } vBin;

    struct sGeomPt
    {
        //etc
    } vGeomPt;

    struct sGeomMultiPt
    {
        //etc
    } vGeomMultiPt;

    struct sGeomPoly
    {
        //etc
    } vGeomPoly;

    struct
    {
        Guid vGuid;
    };
};

bool        UsePool : 1;
bool        OwnPtr  : 1;
FieldType   Type    : 8;

//Other data members, excluded for brevity. 

#define CONSTRUCT : UsePool(true), OwnPtr(false), Type(FieldTypeNull)

Attrib() CONSTRUCT
{}
}   

Now... for reasons I don't understand, whenever an instance of Attrib was made (eg Attrib myAttrib) in VS2013, the compiler would call the the constructor of the class Guid (the last member of the Union).现在......由于我不明白的原因,每当在 VS2013 中创建 Attrib 的实例(例如 Attrib myAttrib)时,编译器都会调用 class Guid(联盟的最后一个成员)的构造函数。 Guids constructor is as follows: Guids构造函数如下:

Guid()
{
    Q1 = 0;
    Q2 = 0;
}

Q1 and Q2 are unsigned int64s and the sole members of Class Guid (variation of Microsoft's GUID). Q1 和 Q2 是无符号的 int64 和 Class Guid(微软 GUID 的变体)的唯一成员。 This had the effect of initializing the Union of Class Attrib to 0, which in turn ended up masking a number of bugs.这具有将 Class 属性的联合初始化为 0 的效果,这反过来又掩盖了许多错误。 To cut a along story short, an uninitialized attribute should be of type Null.简而言之,未初始化的属性应该是 Null 类型。 But in a few places they were wrongly being interpreted as a double or int with a value of zero, or a bool with a value of false, etc, due to the zero initialization.但是在一些地方,由于零初始化,它们被错误地解释为值为 0 的 double 或 int,或值为 false 的 bool 等。

However on switching to VS2019, the Guid constructor is never called (the rest of the code is unchanged and so I assume this is the compiler's "decision").但是在切换到 VS2019 时,永远不会调用 Guid 构造函数(代码的 rest 未更改,因此我认为这是编译器的“决定”)。 As a result the Union component of the Attrib class is not initialized to 0, but instead to massive negative numbers (in the case of ints or doubles), "true" in the case of a bool, etc. This had the effect of unmasking a handful of the previous bugs, ultimately a good thing.结果,属性 class 的联合组件未初始化为 0,而是初始化为大量负数(在整数或双精度的情况下),在布尔的情况下为“真”等。这具有取消屏蔽的效果之前的一些bug,最终还是一件好事。

However, my questions are:但是,我的问题是:

  1. What was the reason for the compiler calling the Guid constructor in 2013, and then no longer doing it in 2019?编译器在 2013 年调用 Guid 构造函数,然后在 2019 年不再这样做的原因是什么? I've not changed any of the code in these classes, so I'm assuming these are changes to the rules of the compiler.我没有更改这些类中的任何代码,所以我假设这些是对编译器规则的更改。 Is there anywhere I can read up on this?有什么地方可以读到这个吗?

  2. The cases that relied on this default 2013 behavior are bugs and I'm in the process of tracking them down.依赖此默认 2013 行为的案例是错误,我正在追踪它们。 However in the mean time I need to replicate the default behavior of the VS2013 build and zero-initialize the Union so that the code can be used.然而,与此同时,我需要复制 VS2013 构建的默认行为并对联合进行零初始化,以便可以使用代码。 I've simply updated the constructor of Attrib.h to call the vUnion struct, which has the effect of zero-initializing everything.我只是简单地更新了 Attrib.h 的构造函数以调用 vUnion 结构,它具有对所有内容进行零初始化的效果。 ie: IE:

    #define CONSTRUCT: UsePool(true), OwnPtr(false), Type(FieldTypeNull), vUnion() #define CONSTRUCT: UsePool(true), OwnPtr(false), Type(FieldTypeNull), vUnion()

This works exactly as I need it to, and seemingly at no noticeable additional cost for a class that is instantiated VERY often.这完全符合我的需要,而且对于经常实例化的 class 来说似乎没有明显的额外成本。 However is there a better way to do it?但是有没有更好的方法呢?

To start with, since Guid has a constructor, the union containing such a Guid is an "Unrestricted union".首先,由于Guid有一个构造函数,包含这样一个Guid的联合是一个“无限制联合”。 In C++98, this was not even allowed.在 C++98 中,这甚至是不允许的。 Also, the union is anonymous.此外, union是匿名的。 This is a problematic combination: unrestricted unions typically require special member functions, but anonymous unions can't have those special member functions.这是一个有问题的组合:不受限制的联合通常需要特殊的成员函数,但匿名联合不能拥有那些特殊的成员函数。

You're mistakingly assuming that the "Guid constructor" is called.您错误地假设调用了“Guid 构造函数”。 It's not;它不是; the union is uninitialized.联合未初始化。 Reading an uninitialized value is Undefined Behavior, anything can happen.读取未初始化的值是未定义的行为,任何事情都可能发生。 "0" is just one of the many possible results. “0”只是众多可能结果之一。

The result could have changed by the phase of the moon.结果可能会因月相而改变。 A new compiler is even more likely to bring its own random new outcomes.新的编译器更有可能带来自己的随机新结果。 There's nothing particular to read;没有什么特别要读的; Undefined Behavior is undefined.未定义行为未定义。 That's literally how it works.这就是它的工作原理。 If there was something to read, it would have been implementation-defined behavior.如果有什么要读的,那将是实现定义的行为。

The "better way" to do this is called std::variant .执行此操作的“更好方法”称为std::variant There's no more need to reinvent the wheel.不再需要重新发明轮子。 These anonymous unions with non-trivial types are very hard to get right, and the alternative is right there in the Standard Library这些具有非平凡类型的匿名联合很难正确处理,而标准库中的替代方法就在那里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM