以下哪些是不确定的行为:

template <class T> struct Struct { T t; };

template <class T> union Union { T t; };

template <class T> void function() {
  Struct aS[10];
  Union aU[10];

  // do something with aS[9].t and aU[9].t including initialization

  T *aSP = reinterpret_cast<T *>(aS);
  T *aUP = reinterpret_cast<T *>(aU);

  // so here is this undefined behaviour?
  T valueS = aSP[9];
  // use valueS in whatever way

  // so here is this undefined behaviour?
  T valueU = aUP[9];
  // use valueU in whatever way

  // now is accessing aS[9].t or aU[9].t now UB?
}

是的,最近3个操作中的哪个是UB?

(我的推理:我不知道该结构,是否有任何要求使其大小与单个元素相同,但是AFAIK联合必须与该元素具有相同的大小。对齐要求我没有对于工会知道,但我猜它是相同的。对于结构我不知道,就工会而言,我猜它不是UB,但正如我所说,我真的不确定。我实际上不知道的结构)

最佳答案

tl; dr:上面代码中的最后两个语句将始终调用未定义的行为,只需将指向联合的指针转换为指向其成员类型之一的指针通常是可以的,因为它实际上并没有做任何事情(最糟糕的是未指定,但绝不会出现不确定的行为;请注意:我们只是在谈论 Actor 本身,使用 Actor 的结果访问对象是一个完全不同的故事)。

根据T最终是什么,在这种情况下Struct<T>可能是标准布局结构[class.prop]/3

T *aSP = reinterpret_cast<T *>(aS);

因为Struct<T>与其第一个成员(类型为T)[basic.compound]/4.3是指针可互换的,所以将是良好定义的。上面的reinterpret_cast等效于[expr.reinterpret.cast]/7
T *aSP = static_cast<T *>(static_cast<void *>(aS));

它将调用数组到指针的转换[conv.array],从而导致Struct<T>*指向aS的第一个元素。然后将该指针转换为void*(通过[expr.static.cast]/4[conv.ptr]/2),然后将其转换为T*,这通过[expr.static.cast]/13是合法的:



同样,
T *aUP = reinterpret_cast<T *>(aU);

如果Union<T>是一个标准布局的联合,它将在C++ 17中得到很好的定义,并且在基于当前标准草案的即将发布的C++版本中,看起来通常已经被很好地定义了,其中联合及其成员之一始终指针可互换的[basic.compound]/4.2

以上所有内容均无关紧要,因为
T valueS = aSP[9];


T valueU = aUP[9];

无论如何都会调用未定义的行为。 aSP[9]aUP[9](根据定义)分别与*(aSP + 9)*(aUP + 9)相同[expr.sub]/1。这些表达式中的指针算法受[expr.add]/4约束


aSPaUP不指向数组的元素。即使aSPaUPT可以指针互换,您也只能访问元素0并计算假设的单元素数组的元素1(但不能访问)的地址…

关于c++ - UB解引用 union 数组时,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55815180/

10-13 06:18