别名和指针互换性

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 不存在,类型 Xint 是指针可相互转换的。因此,编译器必须生成代码,就好像引用可以别名一样。

但是如果成员 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 的引用。 px.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> 是完全不相关的类型。 xp 不能是对同一对象的引用。因此,x.kp.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::kintp是对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> 是不同的类型。无法让 xp 或其部分引用同一对象。

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.ga可以指向同一个G(注意不管b是传值,引用还是指针都是这样).在你的例子中...

template<typename T>
struct X {int k; };
int fn(X<struct A>& x, X<struct B>& p)

.. 唯一的参考文献是 xp。它们指的是不同类型的对象,也就是不同的对象。