🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.std.c++
Article #3486

Re: class.union questions

#3486
From: J. Kanze
Date: Fri, 16 Feb 1996 17:15
279 lines
12941 bytes
In article <v01530501ad3e247b1270@[194.163.74.11]>
dirk@becker.adviser.com (Dirk Becker) writes:

> The first replies to my (DB) original posting seem to have the same
> objection against my interpretation of b). This is my major problem,
> so I put it first now.
> 
> James Kanze (JK) and John Max Skaller (JS) wrote:
> 
> From the WP: [class.union]
> 
> b) "An object of a class with a non-trivial default constructor
>      (_class.ctor_), a non-trivial copy constructor (_class.copy_),
>      a non-trivial destructor (_class.dtor_), or a non-trivial copy
>      assignment operator (_over.ass_, _class.copy_) cannot be a member
>      of a union,"
> 
> DB> This is a major reduction of the restrictions found in ARM, where all
> DB> kinds of constructors or assignment operators were excluded. Here is
> DB> also a note to give the explanation by the assumption, any member
> DB> functions and especially assignments would usually expect a correctly
> DB> constructed object.
> 
> JS>       No. It is merely a more precise statement of the same
> JS> restrictions using more technical terminology.
> 
> JK> This is, in fact, exactly the restriction in the ARM.  All that has
> JK> changes is the way things are described.  In the ARM, classes may or
> JK> may not have constructors (destructors, assignment operators). In the
> JK> current standard, *EVERY* class/struct has a copy constructor, an
> JK> assignment operator and a destructor; the distinction is that some of
> JK> these are trivial.
> 
> I still disagree totally. Please tell me what I'm reading wrong.
> 
> The ARM (mine is an early edition, 1990) does a complete restriction
> against *any* constructor, destructor or user-defined assignment for
> union member objects. (Section 9.5, page 182). The only explanation for
> this rule was already summarized in my original posting, quoted above;
> it has nothing to do with bitwise copy.
> 
> The WP indeed allows member objects with a rich set of constructors
> and assignment operators. It does only <g> cripple the 4 most commonly
> used special member functions, exactly naming them, into trivialness.
> - default constructor
> - destructor
> - copy constructor
> - copy assignment
> I assume this exact enumeration implies all other variants of constructor
> and assignment in member objects are free for user defined versions.

I'm not really sure what the restriction on constructors is meant to
signify.  If the member has a constructor, then the compiler cannot
generate a default constructor for it.  IMHO, this should imply that it
cannot be a member, i.e.: that the words should be that member objects
must have trivial default constructors, and not that they cannot have
non-trivial default constructors.

The problem is how to construct the union.  If the union has no user
provided constructor, then it cannot be constructed, because the
compiler cannot generate a default constructor for it.  But if the union
has a user defined constructor?  Does the user defined constructor have
to initialize the member (presumably yes)?  And what if there are two
such members?  Does the user defined constructor have to initialize both
of them (bad news either way)?

I'll admit to not having thought about the other possible assignment
operators.  Since other assignment operators do not inhibit the
generation of the copy assignment operator, though, the additional
freedom poses no problem.

    [...]
> a)  "A union shall not have base classes. A union shall not be used as
>       a base class."
> 
> JK> Without this rule, you either break the address rule, or you impose
> JK> some special class layout on unions.
> 
> The union has no special class layout? I would say it is very special.
> "a class whose member objects all begin at offset zero"

Poor wording on my part.  This is the address rule that must be
maintained.

> /* my sample of union inheritance */
> 
> JK> This is generally handled better by an abstract base class and
> JK> virtual functions.
> 
> Sure, I like polymorphic objects. But if you have a vast amount of
> really small objects you may get tempted to use the reduced footprint
> of a (1 or 2 byte) tag instead of a sizeof(ptr) vptr.
> It is also slightly faster to load these unions from a binary file
> than from a stream, allocate, dispatch and construct every single
> object and retrieve every single member.

In practice, alignment restrictions are likely to mean that you loose at
least the size of a pointer even if you declare the tag as a byte.

They advantage of using an integral constant tag is non-negligible when
writing to a file, but since in practice, you cannot simply write a
binary image to a file directly anyway, I don't see this as a great
problem.

> /* union with longlong as member */
> 
> DB> If you are lucky to have some native long long datatype, then this
> DB> would be legal code. If you already had to implement your own
> DB> class longlong, now you lost the chance to use it (here).
> 
> JK> This is a problem, but... how would the compiler know when to call
> JK> the constructor/destructor of your longlong?
> 
> Sorry for the wrong sample, longlong may work with trivial c/dtor and
> copy operations. The same is with some class Complex.
> 
> Let's reconsider a class "string" and reference-counter features as
> sideeffects. The answer for your question - how would the compiler
> know? - came with my conclusion as quoted below: Just tell him.
> Here the union constructor itself gets responsible for construction as
> the correct union variant - e.g. choose the correct discriminator tag,
> like "empty".

The problem is that unions (C and C++ unions, at least), don't have
discriminator tags.  If you add one, it is no longer a union.

> DB> I would generally prefer " ... constructors and destructor of member
> DB> objects are ignored, if not explicitly called by the union's ctor/dtor.
> DB> The union's implicit default copy constructor / assignment does a
> DB> binary copy instead of memberwise copy. Objects <containing a vptr>
> DB> cannot be member of a union" - Please pardon my shortcut on vptrs.
> 
> The missing sample for this:
> 
> class Member {
> public:
>   Member();
>   // ...
> };
> class Other {
> public:
>   Other();
>   // ...
> };
> 
> union Union {
>   Union();
>   Union(int);
>   // ...
>   Member m;
>   Other  o;
> };
> 
> Union::Union()
> { // call none of the base member constructors
> }
> 
> // call Member::Member() using a wellknown notation
> Union::Union(int a)
> : m()
> { }
> 
> // deluxe variant - but this would mean a severe extension
> Union::Union(int tag)
> : tag==1 ? m()  // call Member::Member()
> : tag==2 ? o()  // otherwise call Other::Other()
> { }

The extension was discussed, but not adapted.  Ask the Australian
delegation about discriminated unions.

> c) "A union can be thought of as a class"
> 
> DB> Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
> DB> defined copy constructor and copy assignment. The already trivial
> DB> copy constructor/assignment of the member objects will then result
> DB> in one large repetition of copies from and to the same memory locations.
> DB> To avoid this behaviour I would strongly recommend to implement your
> DB> own copy constructor or assignment whenever you use a union, because a
> DB> memberwise copy is usually not desired.
> 
> JK> Basically, if you study the restrictions on unions carefully, the
> JK> rules are such that a bitwise copy *must* be legal for assignment and
> JK> copy construction.  This is the only type of copy the compiler can
> JK> cope with in the absense of a discriminator to tell it which element
> JK> of the union is valid.
> 
> JK> IMHO: if you feel that your union needs a copy constructor or an
> JK> assignment operator, you are probably doing something wrong, in the
> JK> sense that you are trying to use unions in a way that they are not
> JK> intended.  (As they are defined in the standard. If they were defined
> JK> differently, they might have different reasonable uses.)
> 
> I knew, but some compiler implementors forget their homework here.
> If for any reason (generated source) you have a union with lots of
> members, you may accidentally get lots of redundant code. This is
> another reason for implicit bitwise copy.
> 
> And if the trivialness requirement were removed from the copy
> operations, like in my (very draft) replacement text quoted above,
> you would also loose the equivalence.
> 
> From a discussion outside this newsgroup I finally got a hint on the
> intention behind the new restrictions: They should ensure a bitwise
> copy of the union will copy the current union member correctly.
> This is achieved by forcing bitwise copy for any potential member.
> 
> Why did you then not stay just with the POD union? With the
> differentiation between POD union and non-POD union you already
> got the higher degree of complexity, only to undo this with lots
> of additional words.

My position was that a union is not a class; it is an entirely different
type of object, which just happens to have some class-like features.
The position actually adapted, however, is that a union is a class, with
a lot of restrictions.  (It wasn't adapted because there were
alternative proposals that were voted on.  It was adapted because there
was no proposal, and this was the closest to what was in the base
documents, the ARM and the C standard.)

> My first incident with this problem went what I would call a
> user's way instead of the implementor's sight: How can we use
> it? Instead of "How to infer the implicit implementation?"
> Here I felt invited by the WP to use most of the member object
> assignments and than fell into the trivialness trap.
> 
> If your intention is bitwise copy as implicit copy for the union
> itself, why not just call it by its real name instead of hiding
> behind "trivialness"?

Because the standard does not define the idea of bitwise copy; it does
define `trivial' (for constructors, destructors, etc.).  The business
concerning bitwise copy really belongs in a rationale.

> "The implicit union copy constructor and copy assignment apply
> bitwise copy."
> No trivialness restrictions on the member objects!

But you then need something to specify what types of objects can be
safely copied with bitwise copy.  This is simply begging the point.

There was some discussion about allowing anything in a union, but using
bitwise copy anyway.  This was felt to be far too dangerous.

> If someone then decides to have member objects with other copy
> constructor or assignment semantics, he/she just has to provide
> own union copy operations.
> -----
> 
> Some new questions:
> 
> e) "At most one of the member objects can be stored in a union at
>     any time."
> 
> Has anybody already thought about the memberwise implicit operations in
> regard to this rule?
> OK, we'll meet again at the bitwise copy, but this is at least nowhere
> explicitly mentioned in the WP.
> 
> -----
> 
> f)
> 
> Why is [class.mem] 15 restricted to POD structs as union members?
> vptrs are already excluded for union members, and I would still
> ask to exclude them when dropping the trivialness.

References, I suppose.  They also cause problems with bitwise copy.  Of
course, a class or a struct with a reference must have a user defined
constructor, so it presumably cannot be a member of a union (see above).
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Technical Details
Message-ID: <KANZE.96Feb16160724@gabi.gabi-soft.fr>
Path: arc1-ca.pugleaf.net!chronicle.mti.sgi.com!austern
References: <v01530501ad3e247b1270@[194.163.74.11]>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!chronicle.mti.sgi.com!austern
From: kanze@gabi.gabi-soft.fr (J. Kanze)
Newsgroups: comp.std.c++
Subject: Re: class.union questions
Date: 16 Feb 1996 09:15:48 PST
Organization: GABI Software, Sarl.
Approved: austern@isolde.mti.sgi.com
Message-ID: <KANZE.96Feb16160724@gabi.gabi-soft.fr>
References: <v01530501ad3e247b1270@[194.163.74.11]>
NNTP-Posting-Host: isolde.mti.sgi.com
X-Original-Date: 16 Feb 1996 15:07:24 GMT
In-Reply-To: dirk@becker.adviser.com's message of 10 Feb 1996 15:39:23 GMT
X-Auth: PGPMoose V1.1 PGP comp.std.c++
 iQBVAwUBMSS73Ey4NqrwXLNJAQHyvQH9EyHNtQwm2URxZyG+53xFtvs2Z1JndlUf
 mh/Ox+y6n5o/JfsVr4I/5yZvrwdP8iFBp9bmuWCdpVvbq/OtDjwXfA==
 =5HFd
Originator: austern@isolde.mti.sgi.com