别名和指针互换性
Aliasing and pointer-interconvertability
给定以下代码:
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
或其部分引用同一对象。
But what if k is private and X has operator int() const returning k ?
然后什么都没有改变。草率地说,你需要一个 reference/pointer 来获得潜在的别名。例如
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
。它们指的是不同类型的对象,也就是不同的对象。
给定以下代码:
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
或其部分引用同一对象。
But what if k is private and X has operator int() const returning k ?
然后什么都没有改变。草率地说,你需要一个 reference/pointer 来获得潜在的别名。例如
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
。它们指的是不同类型的对象,也就是不同的对象。