为什么这种类型的双关语不是未定义的行为?

Why is this type punning not undefined behavior?

这是一个玩具示例,我认为它会调用未定义的行为:

#include <cstdint>
#include <iostream>
#include <vector>

int
main()
{
    std::vector<uint16_t> foo = {0, 0x42F6};
    std::cout << *reinterpret_cast<float*>(foo.data()) << std::endl;
    return 0;
}

我很确定取消引用 reinterpret_cast 的结果会违反严格的别名规则。然而:

$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
$ g++ -fstrict-aliasing -Wstrict-aliasing -fsanitize=undefined -std=c++14 -o a a.cpp
$ ./a
123

没有来自编译器或 UB 清理程序的警告。为什么不呢?

No warnings from the compiler or the UB sanitizer. Why not?

这并不意味着您没有未定义的行为。编译器和清理器可以尽最大努力检测它,但不能保证捕获它的每一次出现。

您唯一可以确定的方法是阅读标准并检查您对 reinterpret_cast 的用法是否定义明确。

Why is this type punning not undefined behavior?

你的前提是错误的。行为 未定义。

No warnings from the compiler ... Why not?

编译器不需要警告 UB。 有时它确实如此,当星星对齐时,但编译器证明 UB 的存在通常非常昂贵。事实上,如果 可能的,那么语言规则可能会指定程序格式错误。

or the UB sanitizer. Why not?

UB 消毒剂并不完美。它无法检测到所有 UB。考虑提交功能请求以实现对这种情况的检测 - 假设尚未请求。

标准不要求实现来定义某些构造的行为这一事实并不意味着旨在适用于各种目的的实现不应该定义它无论如何。许多实现都有各种选项,这些选项将导致编译器一致地 以有时对编写某些类型代码的程序员有用甚至必要的方式来处理某些此类结构(即使标准确实如此)不需要它)。

关于您的示例,在某些实现中,使用合适的选项组合进行调用会产生与您所见精确定义的行为。在许多实现中,所需的选项将包括 -fno-strict-aliasing,但有些可能还需要其他选项。然而,某些实现,尤其是 vector<uin16_t> 可能不是 32 位对齐的实现,以及如果 float 不是 32 位对齐加载将失败的实现,可能不支持任何选项组合这将定义行为。

然而,问题有点令人困惑的是,即使不使用(或不支持)这些选项,许多实现也会以一种经常(尽管不可靠)一致的方式处理这些构造如果指定并支持这些选项,他们就会这样做。这可能会导致难以确定一段代码是否可以预期始终如一地工作,或者是否可能以不可预测的方式有时工作有时失败。