Re: Empty objects as members
This is a multi-part message in MIME format.
--------------060606040701050607020405
Content-Type: text/plain; charset=ISO-8859-2
Content-Transfer-Encoding: 7bit
On 11/01/10 15:03, Victor Bazarov wrote:
On 11/1/2010 3:50 PM, Larry Evans wrote:
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/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" ;(
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?
--------------060606040701050607020405
Content-Type: text/x-c++src;
name="empty_base.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="empty_base.cpp"
//Purpose:
// Demostrate effect of using empty base class
// in various constructs.
//Reference:
// [EBO] http://www.cantrip.org/emptyopt.html
//Dependencies:
// [boost.variant]
// http://www.boost.org/doc/libs/1_44_0/doc/html/variant.html
// [boost.sandbox.vrtpl.cs]
// https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/boost/composite_storage
//
#include <boost/composite_storage/pack/container_all_of_aligned.hpp>
#include <boost/composite_storage/pack/container_one_of_maybe.hpp>
#include <boost/variant.hpp>
#include <iostream>
template
< unsigned I
>
struct empty_class
/**@brief
* This is an empty class type, according to [EBO].
*/
{
friend
std::ostream&
operator<<
( std::ostream& sout
, empty_class<I>const&
)
{
sout<<"empty_class<"<<I<<">const&";
return sout;
}
unsigned
get(void)const
{
return I;
}
};
struct empties_inherit
: empty_class<0>
, empty_class<1>
/*@brief
* According to [EBO] this class is not an empty class
* because its sequence of base class objects is not empty.
*/
{};
template<unsigned I, typename T>
struct index_inherit
: T
{};
struct empties_tree_inherit
: index_inherit<0,empties_inherit>
, index_inherit<1,empties_inherit>
/**@brief
* Since empties_inherit is not an empty class,
* the empty-base-class-optimization doesn't apply here;
* hence sizeof(empties_tree_inherit)>1.
*/
{};
struct empties_member
{
empty_class<0> m0;
empty_class<1> m1;
};
typedef
char
index_type
;
typedef
boost::mpl::integral_c<index_type,index_type(0)>
index0
;
namespace boost
{
namespace composite_storage
{
template<>
struct enum_base<index0>
{
typedef
char //save space in one_of_maybe
type;
};
}
}
typedef
boost::composite_storage::pack::container
< boost::composite_storage::tags::all_of_aligned
, index0
, empty_class<0>
, empty_class<1>
>
empties_all_of
;
typedef
boost::composite_storage::pack::container
< boost::composite_storage::tags::all_of_aligned
, index0
, empties_all_of
, empties_all_of
>
empties_tree_all_of
;
typedef
boost::composite_storage::pack::container
< boost::composite_storage::tags::one_of_maybe
, index0
, empty_class<0>
, empty_class<1>
>
empties_one_of
;
typedef
boost::variant
< empty_class<0>
, empty_class<1>
>
empties_variant
;
int main(void)
{
std::cout<<"sizeof(empty_class<0>)="<<sizeof(empty_class<0>)<<"\n";
std::cout<<"sizeof(index_inherit<0,empties_inherit>)="
<<sizeof(index_inherit<0,empties_inherit>)<<"\n";
std::cout<<"sizeof(empties_inherit)="<<sizeof(empties_inherit)<<"\n";
std::cout<<"sizeof(empties_tree_inherit)="<<sizeof(empties_tree_inherit)<<"\n";
std::cout<<"sizeof(empties_member)="<<sizeof(empties_member)<<"\n";
std::cout<<"sizeof(empties_all_of)="<<sizeof(empties_all_of)<<"\n";
std::cout<<"sizeof(empties_tree_all_of)="<<sizeof(empties_tree_all_of)<<"\n";
std::cout<<"sizeof(empties_variant)="<<sizeof(empties_variant)<<"\n";
std::cout<<"sizeof(empties_one_of)="<<sizeof(empties_one_of)<<"\n";
std::cout
<<"sizeof(enum_base<index0>::type)="
<<sizeof(boost::composite_storage::enum_base<index0>::type)<<"\n";
{
empties_inherit ei_v;
empty_class<0>*p0=&ei_v;
empty_class<1>*p1=&ei_v;
std::cout<<"p0="<<p0<<"\n";
std::cout<<"p1="<<p1<<"\n";
std::cout<<"*p0="<<*p0<<"\n";
std::cout<<"*p1="<<*p1<<"\n";
}
{
empties_all_of ea_v;
empty_class<0>&r0=ea_v.project<0>();
empty_class<1>&r1=ea_v.project<1>();
std::cout<<"r0="<<r0<<"\n";
std::cout<<"r1="<<r1<<"\n";
std::cout<<"&ea_v="<<&ea_v<<"\n";
std::cout<<"&r0="<<&r0<<"\n";
std::cout<<"&r1="<<&r1<<"\n";
std::cout<<"r1.get()="<<r1.get()<<"\n";
}
return 0;
}
--------------060606040701050607020405--