14

Compiling my code as C++11 with gcc 4.8.2 and llvm/clang 3.4 on fedora-linux, I got strange results that I couldn't really explain... here is a similar program fedora.

#include <iostream>
using namespace std;

struct A {};
struct C {};
struct B1 : A { union { A a;}; };
struct B2 : A { union { C c;}; };

int main()
{
    cout << sizeof(B1) << " " << sizeof(B2) << endl;
}

sizeof(B1) = 2 and sizeof(B2) = 1

But why are the sizes different? Actually I have an idea "why", but I want to find the exact explanation or C++ rule.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
user3608817
  • 141
  • 3

4 Answers4

6

The B1 child has both a parent and sub-object of type A. Two distinct objects of the same type cannot exist at the same address, and the union separately contains an additional A to the parent A.

With B2, the empty base optimization allows the empty A parent and C member to share the single address of the child.

Mark B
  • 95,107
  • 10
  • 109
  • 188
2

I think that there will be helpful two quotes from the C++ Standard. The first one defines what is subobject.

2 Objects can contain other objects, called subobjects. A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element.

The second one says that two subobjects of the same type may not have the same address

A base class subobject may be of zero size (Clause 9); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10). —end note ]

So in this class definition

struct B1 : A { union { A a;}; };

there are two subobjects of type A: base class subobject and member subobject a.

Also it is important to add that every member of every anonymous union is a member of the class containing the anonymous unions.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Is the fact that members of anonymous unions are also direct members of the containing struct/class/union really important here? – Deduplicator May 06 '14 at 17:18
  • @Deduplicator In the last statement of my most starting with words "every member of every..." I cited the Standard. – Vlad from Moscow May 06 '14 at 18:04
  • Could you add which standard you quote? Because n3242, 1.8§6 would allow both interpretations: "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 distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses." – Deduplicator May 06 '14 at 18:27
  • @Deduplicator I quoted Document Number: N3691 Date: 2013-05-16 – Vlad from Moscow May 06 '14 at 18:31
  • Vlad, you got the wrong quotes to prove the point, because notices are non-normative (therefore bracketed), and the paragraph you took from 1.8 is not that relevant. The last paragraph would have been. – Deduplicator May 06 '14 at 18:54
  • @Deduplicato What is the last paragraph? – Vlad from Moscow May 06 '14 at 18:57
  • @Deduplicato 1.8 only gives the definition of the notion of subobject. – Vlad from Moscow May 06 '14 at 18:58
  • Same as for the latest public draft in my answer. – Deduplicator May 06 '14 at 19:00
  • @Deduplicator This quote "...if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses" only confirms what I wrote. So I do not see a problem. – Vlad from Moscow May 06 '14 at 19:04
  • I didn't say your conclusions are wrong, I just said you took the wrong quotes to support your point, and adding the standard version to the answer itself would make it even better. – Deduplicator May 06 '14 at 19:25
  • @Deduplicator The quotes are not wrong. Simply the quoting could be more large. – Vlad from Moscow May 06 '14 at 19:27
1

The C++11 standard can be interpreted to allow size 1 for both examples:

1.8 The C++ object model §6:

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 distinct objects that are neither bit-fields nor base class subobjects of zero size shall have distinct addresses.

There's at least one non-normative notice disallowing it for case 1, but it is non-normative:

10 Derived classes §8:

[ Note: A base class subobject might have a layout (3.7) different from the layout of a most derived object of the same type. A base class subobject might have a polymorphic behavior (12.7) different from the polymorphic behavior of a most derived object of the same type. A base class subobject may be of zero size (Clause 9); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10). —end note ]

The latest publicly available draft (n3797 dated 2013-10-13) though disallows the first example to have size 1:

1.8 The C++ object model §6:

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 that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

thank you guys for quick response!!! I modified my code a bit with template trick so that actual object's data are the same but template types are different - works perfectly!!! for those who interested, now it looks something like this (T must not equal zero):

template<int T> struct A { enum{ val = T < 0 ? -T : T}; };
struct B1 : A<1> { union { A<-1> a;}; };

thank you

user3608817
  • 141
  • 3