У меня есть случай, когда друг бросает объект не базового класса типа "Base" на объект типа класса "Производные", где "Derived" является производным классом "Base" и только добавляет функции, но нет данных. В приведенном ниже коде я добавил элемент данных x к производному классу
struct A {
int a;
};
struct B : A {
// int x;
int x;
};
A a;
int g(B *b) {
a.a = 10;
b->a++;
return a.a;
}
При строгом анализе псевдонимов GCC (также Clang) всегда возвращает 10, а не 11, потому что b никогда не может указывать на a в четко определенном коде. Однако, если я удаляю B::x (как это имеет место в коде моего друга), код ассемблера вывода GCC не оптимизирует обратный доступ a.a и перезагружает значение из памяти. Итак, код моего друга, который называет g "работает" на GCC (как он и предполагал), хотя я думаю, что он по-прежнему имеет поведение undefined
g((B*)&a);
Таким образом, по существу в тех же двух случаях GCC оптимизирует один случай и не оптимизирует другой случай. Это потому, что b может юридически указывать на a? Или это потому, что GCC просто хочет не нарушать реальный код?
Я протестировал ответ, в котором говорится
Если вы удалите B:: x, то B удовлетворяет требованиям в 9p7 для класса стандартного макета, и доступ становится совершенно определенным, потому что два типа являются совместимыми с макетами, 9.2p17.
С двумя совместимыми с макетами перечислениями
enum A : int { X, Y };
enum B : int { Z };
A a;
int g(B *b) {
a = Y;
*b = Z;
return a;
}
Выход ассемблера для g возвращает 1, а не 0, хотя a и b совместимы с макетами (7.2p8).
Итак, мой дальнейший вопрос (цитирование ответа): "Два класса с точно такой же макет могут считаться" почти одинаковыми ", и они не учитываются при оптимизации.". Может ли кто-нибудь предоставить доказательство этому для GCC или Clang?