Why the size of empty class that is derived from t

2019-05-10 23:37发布

问题:

What I know is that size of empty class is 1 just to conform to the standard that does not allow objects (and classes thereof) of size to be 0. Here, I am getting size of a derived class D as 2. Why behavior is different in this case given that there is no data member or virtual pointer inherited from class B and C to D?

#include<iostream>
using namespace std;

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

int main() {
    cout<<"Size of D is: "<<sizeof(D)<<endl;
    return 0;
}

回答1:

To me it seems that whether or not the empty base optimization can be applied here, depends on how one interprets [intro.object/8]:

Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects a and b with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they have distinct addresses.

Are B and C different types? They both are an A as well. Two distinct A objects actually. A compiler writer is allowed to stop right there an allocate storage for B and C separately, without checking that A is empty.

It's worth noting that with g++ the size is back to 1 if you have B and C inherit from separate bases:

Live Example

#include<iostream>

class A  {};
class A1 {};
class B : public A {};
class C : public A1 {};
class D : public B, public C {};

int main() {
    std::cout << "Size of D is: " << sizeof(D) << std::endl;
    return 0;
}


回答2:

This is because you are inheriting from two base classes that have themselves been derived from the same base class A, see how the output changes when you change your program to this

#include<iostream>
using namespace std;

class A {};
class Z {};
class B : public A {};
class C : public Z {};
class D : public B, public C {};

int main() {
    cout<<"Size of D is: "<<sizeof(D)<<endl;
    return 0;
}

As you see now the size of D is 1

The problem is similar to that of the dreaded diamond that you probably know about. The compiler is trying to disambiguate the two instances of A present in D in your example. And the empty base optimization is not applied here, the standard does not require it and the compiler does not implement it (see below)


The standard explicitly allows compilers to not apply the empty base optimization for multiple inheritance cases. Relevant quote from the standard here

Allowing standard-layout classes to have base classes forces compilers to implement the empty base optimization for standard-layout classes, and this could break a compiler's application binary interface (ABI). See 9.2/18 above. This is believed not to be a concern for modern compilers, except possibly in the case of multiple inheritance. Since multiple inheritance is not central to this proposal, allowing standard-layout classes or their bases to use multiple inheritance will be eliminated from the proposal if it proves contentious.