Topic: size of empty class objects


Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/05/11
Raw View
Steve Clamage <stephen.clamage@eng.sun.com> writes:

>An empty struct in C doesn't have much use in practical programming.
>I don't remember ever seeing an empty struct in a C program.  That
>doesn't  mean they have no use, but I'd say it indicates that they
>are very rare.

One reason that empty structs are rare in C is that they are not
permitted by the ANSI/ISO C standard!  GNU C allows them, but
that is a non-standard extension.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kuyper <kuyper@wizard.net>
Date: 1997/05/07
Raw View
meem wrote:
...
> incidentally, another argument against zero-sized structs or classes
> is the classic
>
>   const size_t n_elems = sizeof arr / sizeof arr[0];  // or some variant
>
> which will obviously be a divide-by-zero error now.  once again
> though, the programmer should be smart enough to know what they're
> doing in these sorts of situations.

This is a good argument against zero-sized structs and classes, but I'm
confused by your statement that it would produce a divide-by-zero error
'now'. My copy of paragraph 4, section 1.7 of the draft C++ standard
says:

> Unless it is a bit-field (_class.bit_), a most  derived  object  shall
> have  a  non-zero  size and shall occupy one or more bytes of storage.

By 'now', do you mean some earlier standard? I don't know the revision
history of this paragraph.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Steve Clamage <stephen.clamage@eng.sun.com>
Date: 1997/05/08
Raw View
meem wrote:
>
> "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com> writes:
>
> > Jeff Petkau wrote:
> > >
> > > The only time I can see that nonzero size is actually needed is
> > > the rare case where pointers to different empty class objects
> > > are compared for equality, and the objects weren't allocated at
> > > unique addresses. (This could be prevented by disallowing taking
> > > the address of an empty class; if you really need to take the
> > > address of one, just throw a "char foo" in there and you get the
> > > current behavior.)
> >
> > That's not that rare a situation. Prohibiting taking the address of an
> > empty class, or allowing multiple instances of empty classes to have the
> > same address, would certainly break code that I've written. Not a lot,
> > but some.
> >
>
> i disagree.  i don't think it's a common case at all.  for example,
> C does not specify what the size of an empty `struct' is, and gcc
> makes sizeof() an empty struct == 0.

IMHO the situations in C and C++ regarding empty structs are not
comparable.

An empty struct in C doesn't have much use in practical programming.
I don't remember ever seeing an empty struct in a C program.  That
doesn't  mean they have no use, but I'd say it indicates that they
are very rare.

In C++ empty classes are common and useful. You can encapsulate what
would othewise be global data and functions into a class with only
member functions and static data, or encapsulate a stateless type into
a class that has no data members at all. It would be most unfortunate
if classes that happen to contain no instance data could not be
used in all the same ways as classes that do.

In short, allowing zero-size classes creates program correctness issues
and requires special versions of many kinds of class templates.
Disallowing zero-size classes at worst forces "unneeded" storage to be
used by a program.

The only time the extra storage can be an issue is when you have large
arrays of these classes or many independent objects of that type.
Those are precisely the situations where the zero size becomes a
program correctness issue. For example, you couldn't step through the
array, or compare object addresses to decide identity.

I've seen mention of an implementation possibly being able to arrange
for zero-sized objects to maintain separate identities, but I don't
know of a reasonable way to write such a requirement into the standard.
Whatever the implementation does, you still want to be able to step
through an array and compute the size of an array by using the
expression
sizeof(array)/sizeof(element).

--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Steve Clamage <stephen.clamage@Eng.Sun.COM>
Date: 1997/05/08
Raw View
Steve Clamage wrote:
> >
> > i disagree.  i don't think it's a common case at all.  for example,
> > C does not specify what the size of an empty `struct' is, and gcc
> > makes sizeof() an empty struct == 0.
>
> IMHO the situations in C and C++ regarding empty structs are not
> comparable.
>
> An empty struct in C doesn't have much use in practical programming. ...

A comment from Mike Ball prompted me to check the C standard. It does
not allow empty structs! If g++ does, it would be a local extension.

The C syntax for structs and unions requires at least one member to
be declared. Arrays cannot be declared with zero members. (Violations
require a diagnostic mesage and the program has no defined meaning.) All
simple types have a size >= 1. Put these together and you find that
C does not allow for zero-sized objects, although I didn't find an
explicit statement to that effect.

C++ has always allowed empty classes, and consequently says explicitly
that complete objects must have size >= 1. (Sub-objects, like base-
class components of derived classes, can have zero size.)

--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: comeau@panix.com (Greg Comeau)
Date: 1997/05/09
Raw View
In article <3371FFC1.44BF@Eng.Sun.COM> Steve Clamage <stephen.clamage@Eng.Sun.COM> writes:
>Steve Clamage wrote:
>> >
>> > i disagree.  i don't think it's a common case at all.  for example,
>> > C does not specify what the size of an empty `struct' is, and gcc
>> > makes sizeof() an empty struct == 0.
>>
>> IMHO the situations in C and C++ regarding empty structs are not
>> comparable.
>>
>> An empty struct in C doesn't have much use in practical programming. ...
>
>A comment from Mike Ball prompted me to check the C standard. It does
>not allow empty structs! If g++ does, it would be a local extension.
>
>The C syntax for structs and unions requires at least one member to
>be declared. Arrays cannot be declared with zero members. (Violations
>require a diagnostic mesage and the program has no defined meaning.) All
>simple types have a size >= 1. Put these together and you find that
>C does not allow for zero-sized objects, although I didn't find an
>explicit statement to that effect.

Although I think you are correct in the latter, I think we can agree that
things don't always need to be explicitly said (as I know you know).
But I think it is explicitly stated (see below).

As I recall this issue in C, _the grammer_ might mistakenly lead one to
believe that a member has to be declared. However, I believe it
(ONLY the grammer) does allow cases such as

    struct xyz { typedef int Int; };

or:

    struct xyz { int : 0; };

But I'm pretty sure that's not (semantically) allowed in ISO C,
so something else must cover those cases, as the syntax rule alone
is not sufficient.

>C++ has always allowed empty classes, and consequently says explicitly
>that complete objects must have size >= 1. (Sub-objects, like base-
>class components of derived classes, can have zero size.)

I don't have my C standard in front of me, but I'm close to positive
it also says that objects must be >= 1, all, not just simple types.
If it just said that, then I believe it might possibly be allowed
to interpret that non substantial members can be as if size is 1 or something.
However, that's not needed though because I also recall it says that the
"substantive" members (unlike my examples above then) cannot be the empty set.

- Greg
--
       Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418-3214
               Producers of Comeau C++ 4.0 front-end pre-release
****WEB: http://www.comeaucomputing.com / Voice:718-945-0009 / Fax:718-441-2310
 Here:comeau@comeaucomputing.com / BIX:comeau or comeau@bix.com / CIS:72331,3421
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/05/10
Raw View
meem wrote:
>
> i disagree.  i don't think it's a common case at all.  for example,
> C does not specify what the size of an empty `struct' is, and gcc
> makes sizeof() an empty struct == 0.

It's rare in C, but common in C++, since empty classes are often used as
atom-like tags whose only significance is their address, which must be
unique.

--

Ciao,
Paul

(Please remove the "strip_these_words_" prefix from the return
address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: meem <meem@shep12.wustl.edu>
Date: 1997/05/06
Raw View
"Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com> writes:

> Jeff Petkau wrote:
> >
> > The only time I can see that nonzero size is actually needed is
> > the rare case where pointers to different empty class objects
> > are compared for equality, and the objects weren't allocated at
> > unique addresses. (This could be prevented by disallowing taking
> > the address of an empty class; if you really need to take the
> > address of one, just throw a "char foo" in there and you get the
> > current behavior.)
>
> That's not that rare a situation. Prohibiting taking the address of an
> empty class, or allowing multiple instances of empty classes to have the
> same address, would certainly break code that I've written. Not a lot,
> but some.
>

i disagree.  i don't think it's a common case at all.  for example,
C does not specify what the size of an empty `struct' is, and gcc
makes sizeof() an empty struct == 0.

as jeff said, if the programmer is intentionally creating an empty
struct (or class) it is likely they know what they're doing, and will
be smart enough to allocate a char or something to use in case of any
pointer comparisons.  i think this is just another situation where c++
tries to protect the novice programmer from shooting themselves in the
foot, but instead ends up befuddling the intermediate and advanced.

incidentally, another argument against zero-sized structs or classes
is the classic

  const size_t n_elems = sizeof arr / sizeof arr[0];  // or some variant

which will obviously be a divide-by-zero error now.  once again
though, the programmer should be smart enough to know what they're
doing in these sorts of situations.

meem

--
::  meem@gnu.ai.mit.edu   ::   voice 314/935-1353  ::   pager 800/652-2699  ::
::   pgp fingerprint:   E7 E8 1A 95 F2 06 6A D6   6A 11 44 D6 6A 6B 93 9B   ::
:: GCS v3.12: a-- C+++ UL++++ US++++ UB+++ P++ L+++>++++++ E+>+++ W+ N++ K+ ::
::            w+ O@ M- V- PS+ Y+ PGP++ t+ 5 X R- tv- b DI++ D+ G h+ r- z-   ::
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David Vandevoorde <daveed@vandevoorde.com>
Date: 1997/05/04
Raw View

Jeff Petkau wrote:
> I recently noticed that MSVC 4.2 allocates space for empty classes
> such as the default allocator). My first assumption was that someone
> at Microsoft was just playing a silly joke, and this would be fixed
> in a future release.  But in fact this is required behavior:

Of course. However, you do not _need_ to allocate space
for an no-data allocator. Make it a private base class
instead.

> Why was this decision made? Empty classes are usually used with
> the assumption that they have no overhead (for example, the
> default allocator in the STL). Admittedly it's not much overhead,
> but that's still many, many times more than no overhead at all.
> And in a few cases (eg. expression templates), the difference
> between 0 and 1 byte of overhead can really add up.

I cannot remember hitting that problem when developing
expression templates.

> The only time I can see that nonzero size is actually needed is
> the rare case where pointers to different empty class objects
> are compared for equality, and the objects weren't allocated at
> unique addresses. (This could be prevented by disallowing taking
> the address of an empty class; if you really need to take the
> address of one, just throw a "char foo" in there and you get the
> current behavior.)

So you would also want to disallow arrays of `empty classes'?
That makes for a lot of special rules, and would make the
development of general templates even harder (you might need
a version for an empty class and one for a non-empty class).

 Daveed
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Jeff Petkau <jeffp@notcavedog.com>
Date: 1997/05/05
Raw View

[apologies if I sent out a half-baked reply earlier. Is that the save
key or the send key? Argh.]

> Jeff Petkau wrote:
> > [wondering why empty class objects must have nonzero size]

David Vandevoorde wrote:
> Of course. However, you do not _need_ to allocate space
> for an no-data allocator. Make it a private base class
> instead.

That would remove the overhead in the STL, provided that vendors
actually do it. Are there any STL implementations that derive all the
containers from their allocators? (Would this be permitted by the
standard?)

> > Empty classes are usually used with the assumption that they
> > have no overhead ...  in a few cases (eg. expression templates),
> > the difference between 0 and 1 byte of overhead can really add up.

> I cannot remember hitting that problem when developing
> expression templates.

Are you sure? The templates I use are basically the same as
Todd Veldhuizen's
(http://monet.uwaterloo.ca/~tveldhui/papers/Expression-Templates),
and have significant overhead in the expression constructors.
By specializing all the templates so that instances of empty
classes are not stored (simulating the effect of zero-sized
objects), this overhead can be pretty much eliminated. But to
do this, you have to write two templates for each unary function
and four for each binary function. Ick.

> So you would also want to disallow arrays of `empty classes'?
> That makes for a lot of special rules, and would make the
> development of general templates even harder (you might need
> a version for an empty class and one for a non-empty class).

This would be a nuisance, but again, for the cases where you need
that, you can always add a data member to make the class nonempty.
The extra complexity and rules required are probably a bigger
problem :(

--Jeff Petkau (jeffp at cavedog.com)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Jeff Petkau <jeffp@notcavedog.com>
Date: 1997/05/05
Raw View

David Vandevoorde wrote:

> Jeff Petkau wrote:
> > I recently noticed that MSVC 4.2 allocates space for empty classes
> > such as the default allocator). My first assumption was that someone
> > at Microsoft was just playing a silly joke, and this would be fixed
> > in a future release.  But in fact this is required behavior:

> Of course. However, you do not _need_ to allocate space
> for an no-data allocator. Make it a private base class
> instead.

Are there any implementations of the STL that derive all the containers
from their allocators? (Is that even legal under the standard?)

---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Paul D. DeRocco" <strip_these_words_pderocco@ix.netcom.com>
Date: 1997/05/05
Raw View
Jeff Petkau wrote:
>
> The only time I can see that nonzero size is actually needed is
> the rare case where pointers to different empty class objects
> are compared for equality, and the objects weren't allocated at
> unique addresses. (This could be prevented by disallowing taking
> the address of an empty class; if you really need to take the
> address of one, just throw a "char foo" in there and you get the
> current behavior.)

That's not that rare a situation. Prohibiting taking the address of an
empty class, or allowing multiple instances of empty classes to have the
same address, would certainly break code that I've written. Not a lot,
but some.

--

Ciao,
Paul

(Please send e-mail to mailto:pderocco@ix.netcom.com instead of the
return address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Jeff Petkau <jeffp@notcavedog.com>
Date: 1997/05/03
Raw View
I recently noticed that MSVC 4.2 allocates space for empty classes
such as the default allocator). My first assumption was that someone
at Microsoft was just playing a silly joke, and this would be fixed
in a future release.  But in fact this is required behavior:

[class]
3 A class with an empty sequence of members and base class
  objects is an empty class.  Complete objects and member
  subobjects of an empty class type shall have nonzero size.1)

Why was this decision made? Empty classes are usually used with
the assumption that they have no overhead (for example, the
default allocator in the STL). Admittedly it's not much overhead,
but that's still many, many times more than no overhead at all.
And in a few cases (eg. expression templates), the difference
between 0 and 1 byte of overhead can really add up.

The only time I can see that nonzero size is actually needed is
the rare case where pointers to different empty class objects
are compared for equality, and the objects weren't allocated at
unique addresses. (This could be prevented by disallowing taking
the address of an empty class; if you really need to take the
address of one, just throw a "char foo" in there and you get the
current behavior.)

Or did I just misread something? That's beeen known to happen.

--Jeff Petkau (jeffp at cavedog.com)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]