int 可以别名为 unsigned int 吗?

Can an int be aliased as an unsigned int?

编译器生成的代码假设 int 可以被 unsigned int 别名化。以下代码:

int f(int& a, unsigned int& b){
    a=10;
    b=12;
    return a;
}
int f(int& a, double& b){
    a=10;
    b=12;
    return a;
}

使用 Clang5 生成以下程序集(类似代码由 GCC 或 ICC 生成):

f(int&, unsigned int&): # @f(int&, unsigned int&)
  mov dword ptr [rdi], 10
  mov dword ptr [rsi], 12
  mov eax, dword ptr [rdi]    #return value must be loaded since rdi might equal rsi
  ret
f(int&, double&): # @f(int&, double&)
  mov dword ptr [rdi], 10
  movabs rax, 4622945017495814144
  mov qword ptr [rsi], rax
  mov eax, 10        #return value is a direct value.
  ret

在上面的示例中,在第一个重载 f 中 return 值(在 eax 寄存器中)是 10 或 12 如果 ba 引用同一个对象。在第二次重载中,ab 不能引用同一个对象,因此 return 值始终为 10。

C++标准的这一段表达了严格的别名规则,[intro.object]/8:

[...] Two objects a and b with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they have distinct addresses.

所以根据这个规则,一个 int 不能被一个 unsigned int 别名。

问题:

  1. C++ 标准中是否存在允许 intunsigned int 别名的例外规则?

  2. 如果不是,为什么所有编译器都假设这种可能性?

Is there an exception to this rule in the C++ standard that would allow aliasing of int by unsigned int?

是的,是 [basic.lval]/8:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,