Topic: class.union questions


Author: dirk@becker.adviser.com (Dirk Becker)
Date: 1996/02/10
Raw View
Sorry to be late with this, but I just learned my newsserver
does not yet deliver my posts.

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:



Author: dirk@becker.adviser.com (Dirk Becker)
Date: 1996/02/03
Raw View
Hello, C++ gurus!

In Section 9.6.1 Unions [class.union] there is a large collection of
restrictions on the union itself and on candidate member classes.
I would like to ask for reasons of several of them:

a)  "A union shall not have base classes. A union shall not be used as
     a base class."

This prevents any construction like the following, where one would
like to extend the union contained in some base class:

class GenericInterpreter {
    union opcode {
        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
    };
};

class SpecialInterpreter : public GenericInterpreter {
    union opcode : public GenericInterpreter::opcode {
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

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,"

This is a major reduction of the restrictions found in ARM, where all
kinds of constructors or assignment operators were excluded. Here is
also a note to give the explanation by the assumption, any member
functions and especially assignments would usually expect a correctly
constructed object.

The quoted WP sequence tries its best to exclude virtual function
tables from the union members, which is just fine. But through the
non-trivialness you also exclude _any_ custom make of the copy
constructor and assignment operator.
So you can have any special assignment operator or constructor for
your member objects, any but the most common and useful ones.

Another sample:

class longlong { ... };
struct opcode {
    short tag;
    union {
        short     op1;
        long      op2;
        longlong  op3;
    } data;
};

If you are lucky to have some native long long datatype, then this
would be legal code. If you already had to implement your own
class longlong, now you lost the chance to use it (here).

c) "A union can be thought of as a class"

Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
defined copy constructor and copy assignment. The already trivial
copy constructor/assignment of the member objects will then result
in one large repetition of copies from and to the same memory locations.
To avoid this behaviour I would strongly recommend to implement your
own copy constructor or assignment whenever you use a union, because a
memberwise copy is usually not desired.

Let's combine this with the problems of a) and b):

class GenericInterpreter {
    union opcode {
        opcode(const opcode&);               // replace implicitly defined
        opcode& operator = (const opcode&);  // memberwise copy versions

        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
    };
};

class SpecialInterpreter : public GenericInterpreter {
    union opcode {
        GenericInterpreter::opcode      generic; // error: non-trivial member
        short tag;
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

class ValidInterpreter : public GenericInterpreter {
    union opcode {
        // using copy - paste instead of language features
        short tag;
        struct { short tag; short data; } op1;
        struct { short tag; char* data; } op2;
        //
        struct { short tag; long  data; } op3;
        struct { short tag; float data; } op4;
    };
};

In my opinion, comparing the copy constructor/assignments to others, they
are only special regarding their use by the implicitly defined memberwise
copy constructor/assignment by outside classes. Here I can't find a reason
why a member's custom copy methods should not be applicable instead.

Conclusion, just dreaming:
 I would generally prefer " ... constructors and destructor of member
 objects are ignored, if not explicitly called by the union's ctor/dtor.
 The union's implicit default copy constructor / assignment does a
 binary copy instead of memberwise copy. Objects <containing a vptr>
 cannot be member of a union" - Please pardon my shortcut on vptrs.

d) anonymous union

Why is there no anonymous struct? This is not only for symmetric reasons,
but anonymous structs inside unions may be as useful as anonymous unions
inside structs.


Best regards
Dirk

------------------------------------------------------------------
Dirk Becker                                dirk@becker.adviser.com
Harderweg 76, 22549 Hamburg, Germany          Tel. +49 40 80783143
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/02/06
Raw View
kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:

>dirk@becker.adviser.com (Dirk Becker) writes:
>
>|> In Section 9.6.1 Unions [class.union] there is a large collection of
>|> restrictions on the union itself and on candidate member classes.
>|> I would like to ask for reasons of several of them:
>
>One of the principle goals was that a union be compatible with a C
>union, at least in use.  One of the rules is that the addresses of all
>of the elements in the union compare equal to the address of the union
>itself, when cast to the appropriate type.
>
>|> a)  "A union shall not have base classes. A union shall not be used as
>|>      a base class."
>
>Without this rule, you either break the address rule, or you impose
>some special class layout on unions.

Nevertheless, contrary to what you seem to be implying,
that rule is not necessary for C compatibility.

C++ structs can be very different to C structs, but C compatibility
is preserved for a certain category of C++ structs, namely POD-structs.
In the case of unions, the same thing could have been done -
rule (a) could have been dropped, yet C compatibility could still
have been be supported for POD-unions.

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ 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. ]





Author: kanze@gabi.gabi-soft.fr (J. Kanze)
Date: 1996/02/09
Raw View
In article <4f7l8h$t0e@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus
Henderson) writes:

> kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:
>
> >dirk@becker.adviser.com (Dirk Becker) writes:
> >
> >|> In Section 9.6.1 Unions [class.union] there is a large collection of
> >|> restrictions on the union itself and on candidate member classes.
> >|> I would like to ask for reasons of several of them:
> >
> >One of the principle goals was that a union be compatible with a C
> >union, at least in use.  One of the rules is that the addresses of all
> >of the elements in the union compare equal to the address of the union
> >itself, when cast to the appropriate type.
> >
> >|> a)  "A union shall not have base classes. A union shall not be used as
> >|>      a base class."
> >
> >Without this rule, you either break the address rule, or you impose
> >some special class layout on unions.
>
> Nevertheless, contrary to what you seem to be implying,
> that rule is not necessary for C compatibility.
>
> C++ structs can be very different to C structs, but C compatibility
> is preserved for a certain category of C++ structs, namely POD-structs.
> In the case of unions, the same thing could have been done -
> rule (a) could have been dropped, yet C compatibility could still
> have been be supported for POD-unions.

True.  But there is one intuitive fact that remains true for all
structs: different (data) members have distinct addresses, and in fact
occupy non-overlapping memory.  The same intuitive fact for unions is
that all (non-static) data members have the same address.  (IMHO, of
course.)

There is one restriction that does seem to me totally ungrounded: not
being able to inherit from a union.  I don't think that this feature
would be enormously useful, but I cannot think of a good reason why it
should be banned.  One of the classic solutions for handling C/C++
compatibility is to put all of the data in a PODS, and inherit from it,
using the derived class in C++, but allowing a pointer to it to
automatically become a pointer to the PODS when passed to a C function
declared to take such an address.  The same argument could certainly be
used for inheriting from unions.  If I look in /usr/include/sys, I find
lots of unions which could be potentially used this way.
--
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.  Moderation policy:
  http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]