Псевдоним и указатель - PullRequest
2 голосов
/ 28 января 2020

С учетом следующего кода:

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

Может кто-нибудь объяснить это?

Ответы [ 2 ]

4 голосов
/ 28 января 2020

Здесь

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;
}

on С другой стороны, X<struct A> и X<struct B> - совершенно не связанные типы. x и p не могут быть ссылками на один и тот же объект. Таким образом, x.k и p.k не могут обозначать один и тот же подобъект. Тот факт, что X<struct A>, а также X<struct B> являются взаимозаменяемыми по указателю с int, опять же, не имеет значения…

3 голосов
/ 28 января 2020

Здесь

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 () const, возвращающий 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. Они ссылаются на объекты разных типов, или разные объекты.

...