Question:
Consider the following class:
struct Type {
int a, b, c;
int& ref;
Type(int m) : a(m), b(m), c(m), ref(a) { }
Type(const Type& ot) : a(ot.a), b(ot.b), c(ot.c), ref(a) { }
} obj;
Here we have the sizeof(Type)
is 24
. However obj.ref
can always and in any situation/context be replaced by obj.a
, making the reference resolvable at compile time and making it useless to store the 8 bytes of the reference in the object (and the 4 of padding) . Ideally the sizeof(Type)
can be 12
(only the three int
s).
Can a compiler perform this optimization while strictly following the rules of the standard? Why? Is there any situation where this optimization would be incorrect?
Demonstrate with an example that produces different behavior with/without the optimization.
Answer:
First, the standard states the following in section 8.3.2, paragraph 4:
It is unspecified whether or not a reference requires storage.
So a compiler is free to omit the allocation of a reference whenever that doesn't change the program's behavior. Object size can be 12
.
In the case of the question, the optimization will be valid if and only if the compiler can prove that obj.ref
always results in obj.a
, that is, any construction of a reference in obj.ref
will be done in such a way that it "points" to obj.a
.
References receive an object on startup and cannot be changed until destruction. If a reference is a member, then it has its lifetime equal to the lifetime of the object that contains it, so the only place where it's legal to initialize a reference is at object initialization, this is in the constructor initialization list.
If the compiler can see all the constructors in the class definition and if they all initialize the reference the same way (to a member of the object itself), then there is no way the reference will ever reference another object. So the optimization is valid for the example in the question.