开发者

empty base class optimization

开发者 https://www.devze.com 2022-12-29 23:35 出处:网络
Two quotes from the C++ standard, §1.8: An object is a region of storage. Base class subobjects may have zero size.

Two quotes from the C++ standard, §1.8:

An object is a region of storage.

Base class subobjects may have zero size.

I don't think a region of storage开发者_StackOverflow社区 can be of size zero. That would mean that some base class subobjects aren't actually objects. How do these statements co-exist?


A philosophical argument over the definition of "region" is unnecessary.

1.8/5 says, "Unless it is a bit-field, a most derived object shall have a non-zero size ... Base class sub-objects may have zero size".

So the standard is quite clear what objects (and hence what "regions of storage") can have zero size. If you disagree with the standard what "region" means in English that's one thing, you can fault the authors' (non-programming-related) literary skills. For that matter you can fault their poetic skills (14.7.3/7) But it's quite clear what the standard says here about the sizes of objects of class types.

The pragmatic way to read standards is that given two plausible interpretations of a word, choose the one which doesn't directly contradict another sentence in the same section of the standard. Don't choose the one which matches more closely your personal preferred use of the word, or even the most common use.


C++ does not allow an object of size zero, because every object must have a unique memory address. So if you have:

struct empty {};

// ...

empty myempty;
empty* ptr = &myempty;

then ptr must be pointing to a unique memory address. The standard states the minimum size for an object is 1 byte for this purpose. Similarly, allocations of size 0 are allowed, and return a valid pointer, even if writing to that pointer is not allowed (this works for malloc(0), and new empty returns a pointer to one byte, since sizeof(empty) == 1).

If I derive from empty like so:

struct derived : public empty
{
    int data;
}

There is no longer any point in the base class empty occupying one byte, because all derived will have a unique address due to the data member. The quote "Base class subobjects may have zero size" is present to allow, in this case, for the compiler to not use any space for empty, such that sizeof(derived) == 4. As the title states, it's just an optimization, and it is perfectly legal for the empty part of derived to occupy zero space.


The C++ standard 1.8.5 states :-

Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size. An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.

So, the standard alows a base-class that has no data members (and no virtuals) to share the same address as another subobject with a distinct type. You can play around with the empty base class size like ...

struct a{}; 
struct a1{}; 
struct b : public a, public a1{char c;}; 


int main() 
{ 
  std::cout << sizeof(b) << "\n"; 
  std::cout << sizeof(b::a); 
} 


Which outputs (ignoring padding)...

1 
1 


now try: 


struct a{}; 
struct b : public a {a ax;}; 


int main() 
{ 
  std::cout << sizeof(b) << "\n"; 
  std::cout << sizeof(b::a); 
} 


and the output is ...

2 
1 

because the two instances of a (as a base and as a member) must have distinct addresses.

BTW: "b::a" is another way to say "a". The presence of the scope access operator does not request the "base-class subobject of b of type a". Verse 5.3.3/2 says :- When applying sizeof to a base class subobject, the result is the size of that object's type.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号