如果类型相同,对象地址必须不同,但如果类型不同,地址可以相同

Reason behind that object addresses must differ if types are the same, but addresses can be the same if types differ

本文关键字:如果 地址 类型 对象      更新时间:2023-10-16

我们有空的基类优化。请看以下两种情况:

案例 A

struct Empty1 { };
struct Empty2 { };
struct One: Empty1, Empty2 { };

在这里,sizeof(One)是 1。Empty1Empty2的地址相同。

案例B

struct Empty { };
struct Empty1: public Empty { };
struct Empty2: public Empty { };
struct Two: Empty1, Empty2 { };

在这里,sizeof(Two)是 2。Empty1Empty2的地址不同,因为它们都有Empty(这些Empty应该有不同的地址)。

为什么标准允许不同类型使用相同的地址,但不允许相同类型?此规则避免了什么问题?

如果我创建一个tuple实现,其中所有空成员都将具有相同的地址(无论其类型如何),我会遇到什么问题?

地址是对象标识的一部分。具有单独地址的相同类型的单独对象是一般规则。这就是您如何知道有多个对象。

在复制构造函数中,您通常可以找到自赋值if (this != &other)的测试。如果同一类型的不同对象具有相同的地址,则此操作将失败。

无论如何,不同类型的对象都无法像这样进行比较,所以不是问题。我们还有一个结构体及其第一个成员,一个数组和第一个元素,它们具有相同的地址,但类型不同。

空基类在这里会带来问题,但仅当有两个相同类型的基类,或者基类恰好与派生类的第一个成员属于同一类型时。因此,在这些情况下,有特殊的规则禁止两个相同物体的重叠。

这个想法是它们应该是两个独立的子对象,并在this != &that测试中工作。

该规则在标准 [intro.object] 中说明:

除非对象是

位字段或大小为零的基类子对象,否则该对象的地址是地址 它占用的第一个字节。两个具有重叠生存期但不是位字段的对象 a 和 b 可能具有 如果一个嵌套在另一个中,或者至少有一个是零大小的基类子对象,则使用相同的地址,并且 它们属于不同的类型;否则,它们具有不同的地址。

空基类属于此规则的一个原因是因为 可以将空的基类引用强制转换为基对象的引用。

最好的是看到它的实际效果,我添加了 gcc 生成的程序集和 -O3 优化:

struct Empty1 { };
struct Empty2 { };
struct One: Empty1, Empty2 { 
int i;
};
struct Other{
int i;
};
//an Empty and a One may have the same address                                              
int f(Empty1& a,One& b){   mov     DWORD PTR [rsi], 12  //write to *rsi
b.i=12;                  mov     DWORD PTR [rdi], 15  //may be rsi==rdi
auto& a_one =            mov     eax, DWORD PTR [rsi] //reload from *rsi
static_cast<One&>(a);  ret
a_one.i=15;                         
return b.i;                        
}    
//an int and a One may have the same address                                              
int f(int& a,One& b){      mov     DWORD PTR [rsi], 12  //write to *rsi                           
b.i=12;                  mov     DWORD PTR [rdi], 15  //may be rsi==rdi                         
a=15;                    mov     eax, DWORD PTR [rsi] //reload from *rsi
return b.i;              ret                                                   
}        
//a long can not have the same address as a One 
int f(long& a,One& b){     mov     DWORD PTR [rsi], 12                        
b.i=12;                  mov     eax, 12  //aliasing optimization
a=15;                    mov     QWORD PTR [rdi], 15 //sure rsi!=rdi aliasing rule
return b.i;              ret                        
}   
//the int inside and Other can alias an int inside a One
int f(Other& a,One& b){     mov     DWORD PTR [rsi], 12                        
b.i=12;                  mov     eax, 12  //aliasing optimization
a.i=15;                    mov     QWORD PTR [rdi], 15 //sure rsi!=rdi aliasing rule
return b.i;              ret                        
}                                        

给定地址只能存在一个完整的对象。完整的对象是未嵌套在另一个对象中的对象。

必须在同一地址(没有 UB)以相同的类型反对是不可能的。但是由于您的对象是空的,因此优化的元组中不需要多个对象。您只需要实现一个get<I>,该引用所有Is的同一对象,这些旨在引用同一空类的对象。

相关文章: