I have a case where a friend casts a non-base class object of type "Base" to a class type object "Derived", where "Derived" is a derived class of "Base" and only adds functions, but no data. In the below code, I did add a data member x
to the derived class
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;
}
With strict alias analysis on, GCC (also Clang) always returns 10
, and not 11
, because b
can never point to a
in a well-defined code. However, if I remove B::x
(as is actually the case in the code of my friend), GCC's output assembler code does not optimize the return access of a.a
and reloads the value from memory. So the code of my friend that calls g
"works" on GCC (as he intended) even though I think it still has undefined behavior
g((B*)&a);
So in essentially the same two cases, GCC optimizes one case and doesn't optimize the other case. Is it because b
can then legally point to a
? Or is it because GCC just wants to not break real-world code?
I tested the answer that states
If you remove B::x, then B meets the requirements in 9p7 for a standard-layout class and the access becomes perfectly well-defined because the two types are layout-compatible, 9.2p17.
With two layout compatible enums
enum A : int { X, Y };
enum B : int { Z };
A a;
int g(B *b) {
a = Y;
*b = Z;
return a;
}
The assembler output for g
returns 1
, not 0
, even though A
and B
are layout compatible (7.2p8).
So my further question is (quoting an answer): "two classes with exactly the same layout may be considered "almost the same" and they are left out of the optimization.". Can someone provide a proof of this for GCC or Clang?
g()
called? Withg(&a)
? I don't think using a global and pointer to the same global variable within the same function is anything but "undefined behaviour". And as we know, undefined behaviour can lead to all manner of things, including "what you expected". – Rumeryg((B*)&a)
. In my test snippet, it is not called (I only needed the assembler output forg
) – Mindszentya
may be accessed via expression of typeA
, but not typeB
. – Gessner-std=c++11
instead of-std=gnu++11
. – Downwardsb
object that isn't supposed to be a pointer to ana
object is actually the samea.a
. People prefer fast code for when it is written correctly. – RumeryB
is layout compatible withA
, they consider thatb
ing
might alias globala
, and if it isn't they don't. (And of course, since their is clearly undefined behavior in C++03, they don't need to do this conditionally.) – BrochB
has standard-layout, it's allowed to alias things, and therefore the compiler has to actually generate accesses. – Blastulag
? If not, try doing that. (One way to do it is to storeg
's address in a volatile function pointer variable, then call it from there.) – Billiardsg
. I have just compiled it separately without amain
function. – MindszentyB
does not declare any non static data member or any virtual function, then the compiler supposes that*b
can be an alias fora
. Maybe an early optimization replaces derived class that does not add any data (including vptr) by the base? – Mold