违反严格别名,即使没有任何转换?

Violating strict-aliasing, even without any casting?

我想我真的在问:是别名 "transitive"?如果编译器知道 A 可能是 B 的别名,而 B 可能是 C 的别名,那么它肯定应该记住 A 可能因此是 C 的别名。也许这个 "obvious" 传递逻辑不是必需的?

为清楚起见,举个例子。对我来说,最有趣的例子是严格别名问题:

// g++    -fstrict-aliasing -std=c++11 -O2
#include <iostream>

union
{   
    int i;
    short s;
} u;
int     * i = &u.i;

int main()
{   

    u.i = 1; // line 1
    *i += 1; // line 2

    short   & s =  u.s;
    s += 100; // line 3

    std::cout
        << " *i\t" <<  *i << std::endl // prints 2
        << "u.i\t" << u.i << std::endl // prints 101
        ;

    return 0;
}

g++ 5.3.0,在 x86_64(但不是 clang 3.5.0)上给出了上述输出,其中 *iu.i 给出了不同的数字。但他们应该给出完全相同的数字,因为 i 定义在 int * i = &u.i; 并且 i 不会改变。

我有一个理论:当'predicting' u.i 的值时,编译器会询问哪些行可能会影响u.i 的内容。这显然包括第 1 行。和第 2 行,因为 int* 可以作为联合的 int 成员的别名。还有第 3 行,因为任何可以影响一个联合成员 (u.s) 的东西都会影响同一联合的另一个成员。但是在预测 *i 时,它没有意识到第 3 行会影响 *i 处的 int 左值。

这个理论看起来合理吗?

我觉得这个例子很有趣,因为我没有在其中进行任何转换。我设法通过进行任何转换来打破严格别名。

从联合的非活动成员读取在 C++ 中未定义。 (在 C99 和 C11 中是合法的)。

所以,总而言之,编译器不需要assume/remember任何东西。

标准语:

N4140 §9.5[class.union]/1

In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

只允许从 C++ 中最后写入的联合成员读取。

仅允许在 'similar' 类型(有关详细信息,请参阅 this Q/A)和 char/unsigned char 之间使用联合外部别名。只允许通过char/unsigned char别名其他类型,不允许通过其他类型别名char/unsigned char。如果允许后者,那么所有对象都必须被视为可能对任何其他对象进行别名处理,因为它们可能是 'transitively aliased' 就像您通过 char/unsigned char.[=21= 描述的那样]

但因为情况并非如此,编译器可以安全地假定只有 'similar' 类型的对象和 char/unsigned char 互为别名。