Is a compiler allowed to omit a reference member in a class?

Asked

Viewed 152 times

11

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 that the sizeof(Type) is 24. However obj.ref can always and in any situation/context be replaced by obj.a, so that the reference can be resolved at compile time and make worthless store the 8 bytes of the reference in the object (and the 4 padding). Ideally the sizeof(Type) can be 12 (only the three ints).

A compiler can 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 optimization.

  • I believe that this type of optimization is not allowed, because it moves the layout of the struct/class. But I have to search to give an answer with certainty.

  • I think the same. But, I cannot find any case where this becomes a problem or incompatibility. Note: None of the compilers I have tested do this (obviously).

4 answers

5


First, the standard states the following in section 8.3.2, paragraph 4:

It is unspecified whether or not a Reference requires Storage.

Then a compiler is free to omit the allocation of a reference whenever this does not change the behavior of the program. The size of the object can was 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 this "point" to obj.a.

References receive an object at their initialization and cannot be changed until destruction. If a reference is a member, then it has its life equal to the life of the object that contains it, so the only place where it is legal to initialize a reference is at the initialization of the object, this occurs in the initialization list of constructors.

If the compiler can see all constructs in the class definition and if all they initialize the reference in the same way (for a member of the object itself), so there is no reference at any time to reference another object. Soon the optimization is valid for the example of the question.

  • However compilers do not implement it. Either it is not really valid, or it is very expensive to check its validity to be worth.

  • Or creating a reference within the class to a member of herself is something no one else would do and they don’t have to implement the optimization.

2

Your case has already is was a problematic situation:

#include <iostream>

struct Type {
    int a, b, c;
    int& ref;
    Type(int m) : a(m), b(m), c(m), ref(a) { }
};

int main() {
    Type a(42);
    Type b(a);

    std::cout << &a.ref << "  " << &b.ref << std::endl;
    std::cout << &a.a << "  " << &b.a << std::endl;
}

0xbfb1efa0  0xbfb1efa0
0xbfb1efa0  0xbfb1efb0

http://ideone.com/e65Kt

How the compiler can with sufficient intelligence if it ensures that such optimization would cause the behavior not to change (between the pointer reference implementation or by default) to this example and other possible cases?

More info about possible optimizations in the use of references in: http://www.preney.ca/paul/archives/1051

  • A good copy constructor was missing. Assuming the compiler sees that it exists (see updated issue), your example is no longer valid. Still, nice try

  • @Guilhermebernal my argument would remain the same, it was only an example based on its original code. You are ordering the compiler to perform a proof, so that optimization can then be performed.

  • And there’s still a reference to point somewhere else with this copy builder of mine? If not, then a compiler could prove the valid optimization.

  • @Guilhermebernal I don’t know, I haven’t stopped to think, but another thing would also be to check the pattern regarding layout rules/etc for members and see if there is something that eliminates the need for this line of reasoning.

0

A subclass of Type can overwrite constructor and initialize ref differently. This would invalidate this optimization.

  • 1

    to write a builder? what I see possible as problem situation in his case is to make use of other implicit constructors, but it is not possible to override base constructors.

  • It’s true! It’s not allowed to initialize superclass members in the constructor. SubType(int m) : Type(m), ref(b) {} is not allowed.

  • @C.E.Gesser: Even if these members are public, as in the case of Guilherme’s example?

  • @missingno it is only possible to restart base members in derived constructors, ie it does not work in the initialization list of the derived class constructor, only in the body, after it has already occurred the initialization via base constructor.

0

I see problem if two different compilation drives (two .cpp files) used this one struct (via definition in a file . h, for example), and in one of them the compilation was with optimization and in the other without optimizations. It would be impossible to link the object files generated by the two drives after.

In this way, something like this should be a prohibited optimization, or always mandatory, since if it were optional (and configurable, like other optimizations) it would make impossible the separate compilation model of the linkage in force in the language.

  • 1

    How the definition would be in .hpp, the compiler could detect it in all the files. About ABI, it has always been no standardized and connecting compiler files with different compilers, or different versions, or even different flags can already give problem. So in my view none of these issues would really be a deterrent.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.