如果类型相同,则对象地址背后的原因必须不同,但如果类型不同,则地址可以相同

时间:2017-09-21 11:31:45

标签: c++

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

案例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实现,其中所有空成员将具有相同的地址(无论其类型),我将遇到什么问题?

2 个答案:

答案 0 :(得分:3)

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

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

无论如何都不能比较不同类型的物体,所以不是问题。我们还有一个struct及其第一个成员和一个数组,以及第一个拥有相同地址的元素,但它们的类型不同。

空基类在这里提出了一个问题,但前提是你有两个相同类型的基类,或者基类碰巧与派生类的第一个成员属于同一类型。因此,在这些情况下,有一些特殊规则禁止两个相同对象重叠。

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

答案 1 :(得分:1)

规则在标准[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引用所有{{1}}的同一个对象,它们是指同一个空类的对象。