给定以下代码:
struct Tag {};
struct X {
// Tag t; // if not commented it shouldn't be pointer-interconvertible
int k;
};
int fn(const X& x, int& p) {
int i = x.k;
p = 2;
return i + x.k;
}
生成的代码为:
fn(X const&, int&):
mov eax, DWORD PTR [rdi]
mov DWORD PTR [rsi], 2
add eax, DWORD PTR [rdi]
ret
此处,编译器假定别名。
如果成员t
不存在,则类型X
和int
是指针可相互转换的。因此,编译器必须生成代码,就好像引用可以别名一样。
但是,如果存在成员t
,则它们不应再是指针可相互转换的,并且应生成非别名大小写的代码。但在这两种情况下,除了成员k
的相对地址外,代码是相同的。
汇编程序:
fn(X const&, int&):
mov eax, DWORD PTR [rdi+4]
mov DWORD PTR [rsi], 2
add eax, DWORD PTR [rdi+4]
ret
作为反例
template<typename T>
struct X {int k; };
int fn(X<struct A>& x, X<struct B>& p) {
int i = x.k;
p.k = 2;
return i + x.k;
}
在上面的版本中,生成的代码假定没有别名,但类型是指针可相互转换的。
fn(X<A>&, X<B>&):
mov eax, DWORD PTR [rdi]
mov DWORD PTR [rsi], 2
add eax, eax
ret
谁能解释一下?
这里
int fn(const X& x, int& p) {
int i = x.k;
p = 2;
return i + x.k;
}
编译器必须假定p
可能是对x.k
的引用。p
和x.k
都是类型int
的左值。因此,它们可能相互混叠。无论X
是否与int
指针互转换,都不会改变p
可能是对x.k
的引用这一事实。
这里
int fn(X<struct A>& x, X<struct B>& p) {
int i = x.k;
p.k = 2;
return i + x.k;
}
另一方面,X<struct A>
和X<struct B>
是完全不相关的类型。x
和p
不能是对同一对象的引用。因此,x.k
和p.k
不能表示同一个子对象。事实上,X<struct A>
和X<struct B>
都可以与int
进行指针互转换,这同样是无关紧要的......
这里
int fn(const X& x, int& p) {
int i = x.k;
p = 2;
return i + x.k;
}
X::k
是int
,p
是对int
的引用。p
可以是对x.k
的引用。
另一方面,在这里:
int fn(X<struct A>& x, X<struct B>& p) {
int i = x.k;
p.k = 2;
return i + x.k;
}
X<struct A>
和X<struct B>
是不同的类型。没有办法让x
和p
或它的一部分引用同一个对象。
但是,如果 k 是私有的并且 X 有运算符 int(( 常量返回 k 怎么办?
然后什么都没有改变。说得很草率,你需要一个引用/指针来获得潜在的混叠。例如
struct G {};
struct H { G* g; }
void foo(G* a,H b);
在这里,b.g
和a
可以指向相同的G
(请注意,无论b
是通过值、引用还是指针传递的,都是这种情况(。在您的示例中...
template<typename T>
struct X {int k; };
int fn(X<struct A>& x, X<struct B>& p)
.. 唯一的参考文献是x
和p
.它们指的是不同类型的对象,也就是不同的对象。