使用 reinterpret_cast 的符号别名

Signedness aliasing using reinterpret_cast

取下面代码

#include <iostream>

void func() {
    int i = 2147483640;
    while (i < i + 1)
    {
        std::cerr << i << '\n';
        ++i;
    }

    return;
}

int main() {
    func(); 
}

这段代码显然是错误的,因为 while 循环只有在带符号的 int i 溢出时才能终止,这是 UB,因此编译器可能会将其优化为无限循环(Clang 在-O3),或者做其他时髦的事情。我现在的问题是:根据我对 C++ 标准的阅读,等同于符号 的类型可能 别名(即指针 int*unsigned* 可能是别名)。为了做一些时髦的签名“包装”,以下是否有未定义的行为?

#include <iostream>

static int safe_inc(int a)
{
    ++reinterpret_cast<unsigned&>(a);
    return a;
}

void func() {
    int i = 2147483640;
    while (i < safe_inc(i))
    {
        std::cerr << i << '\n';
        ++i;
    }

    return;
}

int main() {
    func(); 
}

我已经在 -O3 上使用 Clang 8 和 GCC 9 以及 -Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined 参数尝试了上述代码,没有收到任何错误或警告,并且循环在包装到 INT_MIN 后终止。

cppreference.com 告诉我

Type aliasing

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.

根据我的阅读,这意味着出于类型别名的目的,不考虑符号性,并且使用 reinterpret_cast 的代码具有明确定义的语义(尽管无论如何有点俗气)。

在这里使用别名是完全合法的。见 http://eel.is/c++draft/expr.prop#basic.lval-11.2:

If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:53

(11.1) the dynamic type of the object,

(11.2) a type that is the signed or unsigned type corresponding to the dynamic type of the object

我觉得,其实溢出问题也值得一谈,不一定需要reinterpret_cast。使用隐式整数转换可以实现完全相同的效果

 unsigned x = i;
 ++x;
 i = x; // this would serve you just fine.

此代码将由实现定义 pre-C++20,因为您将从目标类型无法表示的值进行转换。

自 C++20 起,此代码将为 well-formed。

https://en.cppreference.com/w/cpp/language/implicit_conversion

附带说明一下,如果您想要整数溢出语义,不妨从无符号类型开始。

您的代码完全合法,cpp 参考是一个很好的来源。您可以在标准 [basic.lval]/11

中找到相同的信息

If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:

  • the dynamic type of the object,

  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,[...]