下面给出了详细的代码,但基本上是:我有一个类,其中包含一些未初始化的数据(未在构造函数中设置)。在某些情况下复制此类的实例时,GCC 不会发出警告,即使它正在读取未初始化的数据来执行此操作。然后,当将数据包装在
union
中时,GCC does 发出警告,但据我了解,这是标准 does 进行定义行为的一种情况。因此,在第二种情况下,它似乎应该不发出警告。
换句话说,GCC 的行为似乎与应有的完全相反。
旁白:我意识到这是在处理未定义的行为,所以编译器不欠我任何东西,但鉴于GCC对这种情况有特定的警告,他们反对(我的解释)似乎很奇怪标准。
请帮助我理解正在发生的问题。
这里是演示该问题的 godbolt 实时链接:https://godbolt.org/z/Kdaa8M8qa
注意
-Wuninitialized
的使用。
struct MyObj {
MyObj()
// leave data uninitialized
//: val(123), fval(9.5f)
{}
// Note: compiler-provided copy constructor
//union {
int val;
float fval;
//};
};
在这里,当我们将此对象放入包含对象中,并复制该容器的实例时,我们会收到来自 GCC 的 no 警告,即使
MyObj::val
和 fval
显然未初始化,并复制它们因此显然是UB。我希望 -Wuninitialized
能够抓住这个明显的问题。
请参阅上面给出的 godbolt 链接,查看
Container
包装类的使用。
struct MyObj {
// ...
// same as above
// ...
union {
int val;
float fval;
};
};
在这里,我们与上面相同,但是将成员包装在一个联合体中。 根据我对标准的理解,工会得到特殊待遇,并且他们复制了“对象表示”(就像
memcpy
所做的那样)。因此,我们可以通过这种方式复制未初始化的值,即使它们具有陷阱表示等。
然而,令人困惑的是,通过这一更改,现在 GCC 发出了警告。
所以,我想知道:GCC 在这两种情况下的行为都是正确的,而我的理解是错误的吗?如果是这样,请详细解释原因。
错误并不是说复制分配是未初始化的使用。
例如,这没有警告:
cont = Container{ 10, {} };
cont.o.val = 2; // <-- This is the added line
printf("Internal undefined val: %d (0x%x)\n", cont.o.val, cont.o.val);
警告应该是说
<anonymous>
(来自 Container{ 10, {} }
的临时物化)在未初始化时正在读取 ,与 printf
行中。
虽然错误应该显示类似
<temporary>.Container::o.MyObj::<anonymous>.MyObj::<unnamed union>::val
的内容并指向 printf
行,而不是创建临时文件的行(也许它应该有两个位置?)
如果您使用构造函数而不是赋值,它确实会告诉您正确的行:
Container cont = Container{ 10, {} };
// cont.o.val = 2;
printf("Internal undefined val: %d (0x%x)\n", cont.o.val, cont.o.val);
<source>:50:11: warning: 'cont.Container::o.MyObj::<anonymous>.MyObj::<unnamed union>::val' is used uninitialized in this function [-Wuninitialized]
printf("Internal undefined val: %d (0x%x)\n", cont.o.val, cont.o.val);
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~