Empty objects as members

J

Juha Nieminen

I have got the impression from somewhere that if you have an empty
class (in other words one without any member variables) and you use
it as a member variable of another class, this empty object will take
at least one byte of space. In other words, the compiler is *not* allowed
to optimize it to taking no space at all. (The only situation where the
compiler is allowed to optimize it is inheritance, this being the so-called
"empty base class optimization".)

However, this is only an impression I got. I would like a confirmation
from the C++ standard. Could someone kindly refer me to the proper
section?
 
V

Victor Bazarov

I have got the impression from somewhere that if you have an empty
class (in other words one without any member variables) and you use
it as a member variable of another class, this empty object will take
at least one byte of space. In other words, the compiler is *not* allowed
to optimize it to taking no space at all. (The only situation where the
compiler is allowed to optimize it is inheritance, this being the so-called
"empty base class optimization".)

However, this is only an impression I got. I would like a confirmation
from the C++ standard. Could someone kindly refer me to the proper
section?

I think you are asking about 1.8 The C++ Object model, paragraphs 4 and
5 that describe what "the most derived object" is and that its size is
non-zero and that it occupies one or more bytes of storage.

V
 
B

Balog Pal

"Juha Nieminen"
I have got the impression from somewhere that if you have an empty
class (in other words one without any member variables) and you use
it as a member variable of another class, this empty object will take
at least one byte of space. In other words, the compiler is *not* allowed
to optimize it to taking no space at all. (The only situation where the
compiler is allowed to optimize it is inheritance, this being the
so-called
"empty base class optimization".)

However, this is only an impression I got. I would like a confirmation
from the C++ standard. Could someone kindly refer me to the proper
section?

1.8p5

Unless it is a bitfield (9.6), a most derived object shall have a nonzero
size and shall occupy one or more bytes of storage. Base class subobjects
may have zero size. An object of POD4) type (3.9) shall occupy contiguous
bytes of storage.
 
L

Larry Evans

On 11/01/10 13:35, Balog Pal wrote:
[snip]
1.8p5

Unless it is a bitfield (9.6), a most derived object shall have a
nonzero size and shall occupy one or more bytes of storage. Base class
subobjects may have zero size. An object of POD4) type (3.9) shall
occupy contiguous bytes of storage.

Balog,

Would you happen to know the rationale for the different sizes used
for a base object and most derived object? It seems it would save
some space; do, why not save the space?

TIA.

-Larry
 
V

Victor Bazarov

On 11/01/10 13:35, Balog Pal wrote:
[snip]
1.8p5

Unless it is a bitfield (9.6), a most derived object shall have a
nonzero size and shall occupy one or more bytes of storage. Base class
subobjects may have zero size. An object of POD4) type (3.9) shall
occupy contiguous bytes of storage.

Balog,

Would you happen to know the rationale for the different sizes used
for a base object and most derived object? It seems it would save
some space; do, why not save the space?

What would save the space? Making different members of some class have
the same address? Base class subobject is allowed to be of zero size
because in some cases its address can coincide with the address of the
derived object without introducing any ambiguities. Two base classes
[of the same derived class] cannot have the same address. Two members
of the same class cannot have the same address. Please elaborate on
your "space saving" idea.

V
 
M

Marcel Müller

Victor said:
What would save the space? Making different members of some class have
the same address? Base class subobject is allowed to be of zero size
because in some cases its address can coincide with the address of the
derived object without introducing any ambiguities.

I see, and I know of this restriction, of course.

But why must a pointer to a non polymorphic object be unique? They do
not support dynamic casts anyway. And why does this not apply to one
base class?


Marcel
 
V

Victor Bazarov

I see, and I know of this restriction, of course.

But why must a pointer to a non polymorphic object be unique? They do
not support dynamic casts anyway. And why does this not apply to one
base class?

I probably don't understand the question, but why would polimorphic-ness
matter?... Data members in the same object and elements of the same
array wouldn't be distinguishable if pointers weren't unique.

V
 
H

Howard Hinnant

Saving space for empty data members has long been an optimization that
some people thought was worth fighting for. Every implementation of
every std::container I'm currently aware of optimizes away the space
for an empty allocator (for example).

The first I ever read about a general solution for this problem is
here:

http://www.cantrip.org/emptyopt.html

Next came boost::compressed_pair:

http://www.boost.org/doc/libs/1_44_0/libs/utility/compressed_pair.htm

And lately implementors have started giving the C++0x std::tuple this
optimization. All of the implementations are refinements of Nathan's
1997 paper; using derivation to achieve the optimization.

-Howard
 
M

Marc

Victor said:
What would save the space? Making different members of some class have
the same address? Base class subobject is allowed to be of zero size
because in some cases its address can coincide with the address of the
derived object without introducing any ambiguities. Two base classes
[of the same derived class] cannot have the same address.

Are you sure about this last one? g++ seems to do the empty base class
optimization even if there are many such empty base classes.
 
M

Marcel Müller

Victor said:
I probably don't understand the question, but why would polimorphic-ness
matter?...

Classes with at least one virtual function are guaranteed to support
RTTI. For this to work the relation between objects and pointers must be
bijective.

Data members in the same object and elements of the same
array wouldn't be distinguishable if pointers weren't unique.

In my opinion there is no need to have unique pointer for zero length
objects, since non polymorphic objects always must have a static type,
known at compile time.

Most probably I missed something, but I don't know what.


Marcel
 
L

Larry Evans

On 11/01/10 13:35, Balog Pal wrote:
[snip]
1.8p5

Unless it is a bitfield (9.6), a most derived object shall have a
nonzero size and shall occupy one or more bytes of storage. Base class
subobjects may have zero size. An object of POD4) type (3.9) shall
occupy contiguous bytes of storage.

Balog,

Would you happen to know the rationale for the different sizes used
for a base object and most derived object? It seems it would save
some space; do, why not save the space?

What would save the space? Making different members of some class have
the same address?
Yes.

Base class subobject is allowed to be of zero size
because in some cases its address can coincide with the address of the
derived object without introducing any ambiguities. Two base classes
[of the same derived class] cannot have the same address. Two members
of the same class cannot have the same address.
Why?

Please elaborate on
your "space saving" idea.

I suppose there's something dangerous about two members of the same
class having the same address, but I don't know what that danger is.

I've read:

http://www.cantrip.org/emptyopt.html

which says:

If you were keeping track of separate objects by their addresses, f.b
and f.a[0] would appear to be the same object. The Standard committee
chose to finesse these issues by forbidding zero-sized addressable
objects.

However, if a program has no need to keep track of separate objects by
their addresses, then what would be the harm of having two members with
the same address?

The real reason I'm interested is because:


https://svn.boost.org/trac/boost/br...ite_storage/pack/container_all_of_aligned.hpp

can have something like 0 sized objects but they're really just
offsets into a buffer. The offset's are cast to the actual type
(properly aligned) and I'm worried that doing that is dangerous
for empty class objects somehow and I'd like to know what that
danger is before it "bites me" ;(

Thanks for any enlightenment you could provide, Victor.

-Larry

PS, to illustrate, the attachment, when run, produces output:

sizeof(empty_class<0>)=1
sizeof(index_inherit<0,empties_inherit>)=1
sizeof(empties_inherit)=1
sizeof(empties_tree_inherit)=2
sizeof(empties_member)=2
sizeof(empties_all_of)=1
sizeof(empties_tree_all_of)=1
sizeof(empties_variant)=8
sizeof(empties_one_of)=1
sizeof(enum_base<index0>::type)=1
p0=0x7fff0fa29def
p1=0x7fff0fa29def
*p0=empty_class<0>const&
*p1=empty_class<1>const&
r0=empty_class<0>const&
r1=empty_class<1>const&
&ea_v=0x7fff0fa29dee
&r0=0x7fff0fa29dee
&r1=0x7fff0fa29dee
r1.get()=1

which illustrates that empty members occupy 1 byte
(the empties_member struct sizeof=2), but empties_inherit
does use EBO resulting in sizeof=1.

The empties_all_of sizeof=1 despite being similar
to empties_member.

However, is there some downside to empties_all_of?
 
V

Victor Bazarov

On 11/01/10 13:35, Balog Pal wrote:
[snip]
1.8p5

Unless it is a bitfield (9.6), a most derived object shall have a
nonzero size and shall occupy one or more bytes of storage. Base class
subobjects may have zero size. An object of POD4) type (3.9) shall
occupy contiguous bytes of storage.


Balog,

Would you happen to know the rationale for the different sizes used
for a base object and most derived object? It seems it would save
some space; do, why not save the space?

What would save the space? Making different members of some class have
the same address?
Yes.

Base class subobject is allowed to be of zero size
because in some cases its address can coincide with the address of the
derived object without introducing any ambiguities. Two base classes
[of the same derived class] cannot have the same address. Two members
of the same class cannot have the same address.
Why?

Please elaborate on
your "space saving" idea.

I suppose there's something dangerous about two members of the same
class having the same address, but I don't know what that danger is.

My understanding is that the object is allowed to have behavior based on
the information provided to it by the system during run-time. For instance,

struct Hand {
void tap_fingers() const;
};

struct Man {
Hand left_hand, right_hand;
void tap_fingers(Hand* which_hand) {
if (which_hand == &right_hand) // do only if my right hand
which_hand->tap_fingers();
}
};

Imagine that in some environment where the size of 'Hand' is 0 and the
size of 'Man' is 0 as well (since it only has two objects of size 0, I
can give the man a command to tap some other Man's hand. That could
easily be against the model, so I would have to introduce some kind of
artificial discriminator to the Hand. Currently I don't need to.
I've read:

http://www.cantrip.org/emptyopt.html

which says:

If you were keeping track of separate objects by their addresses, f.b
and f.a[0] would appear to be the same object. The Standard committee
chose to finesse these issues by forbidding zero-sized addressable
objects.

However, if a program has no need to keep track of separate objects by
their addresses, then what would be the harm of having two members with
the same address?

Yes, if it doesn't, then... But how do you determine which class does
and which doesn't? It's quite difficult to deduce from the code, no? I
mean, for the compiler. So, it's easier to just always have 1 byte.
The real reason I'm interested is because:


https://svn.boost.org/trac/boost/br...ite_storage/pack/container_all_of_aligned.hpp

can have something like 0 sized objects but they're really just
offsets into a buffer. The offset's are cast to the actual type
(properly aligned) and I'm worried that doing that is dangerous
for empty class objects somehow and I'd like to know what that
danger is before it "bites me" ;(

Yeah, I don't know. I guess you can have an algorithm that doesn't care
about the addresses of individual objects in the current language
definition. Just have a class with all static members.
Thanks for any enlightenment you could provide, Victor.

I'm afraid I'm myself in the dark about it :-/

V
 
L

Larry Evans

On 11/1/2010 7:29 PM, Larry Evans wrote: [snip]
I suppose there's something dangerous about two members of the same
class having the same address, but I don't know what that danger is.

My understanding is that the object is allowed to have behavior based on
the information provided to it by the system during run-time. For
instance,

struct Hand {
void tap_fingers() const;
};

struct Man {
Hand left_hand, right_hand;
void tap_fingers(Hand* which_hand) {
if (which_hand == &right_hand) // do only if my right hand
which_hand->tap_fingers();
}
};

Imagine that in some environment where the size of 'Hand' is 0 and the
size of 'Man' is 0 as well (since it only has two objects of size 0, I
can give the man a command to tap some other Man's hand. That could
easily be against the model, so I would have to introduce some kind of
artificial discriminator to the Hand. Currently I don't need to.

OK, in this special case; however, I was wondering if there's some
situation where two members having the same address would cause
undefined behaviour. In this cause, it just means you can't
discriminate between left_hand and right_hand based on address.

Of course if one had to discriminate on the basis of the address, one
could just add a dummy char to Hand to force it to occupy 1 byte.
I've read:

http://www.cantrip.org/emptyopt.html

which says:

If you were keeping track of separate objects by their addresses, f.b
and f.a[0] would appear to be the same object. The Standard committee
chose to finesse these issues by forbidding zero-sized addressable
objects.

However, if a program has no need to keep track of separate objects by
their addresses, then what would be the harm of having two members with
the same address?

Yes, if it doesn't, then... But how do you determine which class does
and which doesn't? It's quite difficult to deduce from the code, no? I
mean, for the compiler. So, it's easier to just always have 1 byte.
The real reason I'm interested is because:


https://svn.boost.org/trac/boost/br...ite_storage/pack/container_all_of_aligned.hpp


can have something like 0 sized objects but they're really just
offsets into a buffer. The offset's are cast to the actual type
(properly aligned) and I'm worried that doing that is dangerous
for empty class objects somehow and I'd like to know what that
danger is before it "bites me" ;(

Yeah, I don't know. I guess you can have an algorithm that doesn't care
about the addresses of individual objects in the current language
definition. Just have a class with all static members.

To be honest, originally, the container_all_of_aligned.hpp code
mentioned in my previous post couldn't have 0 sized members; however,
since boost spirit removes attributes with no data(Unused attributes):

http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit
/karma/quick_reference/compound_attribute_rules.html

I thought it would be useful to allow 0 sized members in the
container_all_of_aligned. So far, I can see no disadvantage
except maybe the extra template metaprogramming; however, I
would think that spirit, in order to remove these Unused
attributes, would have to do some template metaprogramming also.
So, maybe there's no disadvantage w.r.t. Spirit. (BTW, I have hinted
at this to the Spirit deval ml).
I'm afraid I'm myself in the dark about it :-/

OK, but thanks for considering the question.

-L
 
B

Bo Persson

Marc said:
Victor said:
What would save the space? Making different members of some class
have the same address? Base class subobject is allowed to be of
zero size because in some cases its address can coincide with the
address of the derived object without introducing any ambiguities.
Two base classes [of the same derived class] cannot have the same
address.

Are you sure about this last one? g++ seems to do the empty base
class optimization even if there are many such empty base classes.

Many empty base classes of the same type?

Objects of different types can have the same address, like a class
object and its first member. Or a class object and its empty base
class.

But not several objects of the same type. Having the same address
would make them the same object!


Bo Persson
 
B

Bo Persson

Victor said:
On 11/1/2010 7:29 PM, Larry Evans wrote:

Yes, if it doesn't, then... But how do you determine which class
does and which doesn't? It's quite difficult to deduce from the
code,
no? I mean, for the compiler. So, it's easier to just always have
1 byte.

But if you don't care about the identity of the member objects, why
would you have two members of that type in the first place?


Bo Persson
 
B

Bo Persson

Bo said:
But if you don't care about the identity of the member objects, why
would you have two members of that type in the first place?


Bo Persson

This was meant as a reply to Larry, not to Victor.


Bo Persson
 
L

Larry Evans

Bo said:
Victor said:
On 11/1/2010 7:29 PM, Larry Evans wrote:
However, if a program has no need to keep track of separate
objects by their addresses, then what would be the harm of having
two members with the same address?
[snip]
But if you don't care about the identity of the member objects, why
would you have two members of that type in the first place?


Bo Persson

This was meant as a reply to Larry, not to Victor.
[snip]

Good question! In my reply to Victor, I mentioned my motivation was
to emulate what Spirit did for spirit::unused. However, the
container_all_of_aligned, currently, instead of removing the unused
attribute(or actually, type, T, such that boost::is_empty<T>), simply
provides no space for it. Now, I could have removed it, like Spirit;
however, it just seemed simpler to just provide no space for it and
achieve the same space saving that Spirit gets. I could be wrong, but
it's worth a try ;)
 
J

James Kanze

Marc said:
Victor Bazarov wrote:
What would save the space? Making different members of some class
have the same address? Base class subobject is allowed to be of
zero size because in some cases its address can coincide with the
address of the derived object without introducing any ambiguities.
Two base classes [of the same derived class] cannot have the same
address.
Are you sure about this last one? g++ seems to do the empty base
class optimization even if there are many such empty base classes.
Many empty base classes of the same type?
Objects of different types can have the same address, like a class
object and its first member. Or a class object and its empty base
class.
But not several objects of the same type. Having the same
address would make them the same object!

Exact. If the first member of the class has the same type as
a base, for example, the empty base class optimization doesn't
apply. (And g++ handles this correctly.)
 
J

Joshua Maurice

Exact.  If the first member of the class has the same type as
a base, for example, the empty base class optimization doesn't
apply.  (And g++ handles this correctly.)

I wondering if anyone can point to the parts of the (C++) standard
which require this. I'm recalling a conversation how this might have
been removed in a newer draft (or something), and I was wanting to
track this down.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top