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
--
I do not respond to top-posted replies, please don't ask
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
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.
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
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.
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
> 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.
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
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:
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" ;(
>
> V
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?
Most probably *I* didn't give it enough thought. What you're saying, I
think, is that if an object doesn't have a state and is not polymorphic
(IOW, doesn't have member data that can have [different] values or can
have a different dynamic type), there is no need to allocate storage for
it, right? To extend this reasoning, the member functions for such a
class don't have to have a unique 'this' pointer, since there is no
*need* to distinguish between objects (they are all the same), yes? The
objects of that type don't have identity, but the question to answer is,
"why would then need it?" The compiler then can optimize and reuse some
pointer for all 'this' pointers when calling their member functions,
yes? Now, imagine that the identity is not stored (state) but implied
in the behavior, like
int NoIdentityClass::foo(NoIdentityClass const& other) const
{
if (this == &other)
return 0;
else
return 42;
}
how would the compiler know not to optimize? I am not sure what the use
of such algorithm would be (so it could be just theoretical), but it's
quite real. I can write a program to have a limited number of some
stateless objects that would require to be unique, so I would need to
disallow the compiler to optimize the size to 0 for *that* particular
class. I am guessing that requiring the compiler to always allocate at
least one byte for those is both *inexpensive* and solves the identity
problem.
Anyway, interesting. Thanks for making me think about it.
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/browser/sandbox/variadic_templates/boost/composite_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.
>
>>
>> V
>
> Thanks for any enlightenment you could provide, Victor.
I'm afraid I'm myself in the dark about it :-/
> -Larry
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/browser/sandbox/variadic_templates/boost/composite_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).
>>
>>>
>>> V
>>
>> Thanks for any enlightenment you could provide, Victor.
>
> I'm afraid I'm myself in the dark about it :-/
>
OK, but thanks for considering the question.
-L
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
>> 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.
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
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 ;)
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.)
--
James Kanze
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.