Topic: union of user defined type


Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1998/10/29
Raw View

David R Tribble wrote in message <36379A89.642@noSPAM.central.beasys.com>...
>Siemel Naran wrote:
>> union X { A a; B b; }; // sizeof(X)==max(sizeof(A),sizeof(B))
>>      X x; // calls X::X() which in turn calls A::A()
>
>Why does the definition of x invoke A::A() but not B::B()?


This would correspond to the rule for static unions which, by default,
initialize their first member to zero (not necessarily all-bits zero), and
leave other members in an undefined state.



[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1998/10/30
Raw View
On 29 Oct 98 05:57:01 GMT, David R Tribble
>Siemel Naran wrote:

>> union X { A a; B b; }; // sizeof(X)==max(sizeof(A),sizeof(B))
>>
>> int main()
>> {
>>      X x; // calls X::X() which in turn calls A::A()
>>      X y(x); // copies sizeof(X) bytes from 'x' to 'y'
>> } // calls x.~X() and y.~X() which of course do nothing


>Why does the definition of x invoke A::A() but not B::B()?

The ctor for either A or B must be called when instantiating an X
object.  We have a precedent for this.  When we create a union at
the class static, namespace, global, or function static level, it
is the first element that is default initialized to zero.

[ 8.5.  Initializers ]

5  To zero initialize storage for an object of type T means:

   -- if T is a scalar type, ...
   -- if T is a non-union class type, ...
   -- if T is a union type, the storage for its first data element is
      zero initialized.


And remember that a union can have a ctor too.  This ctor can take
arguments and initialize either the A or B object.  The following
program compiles and runs under existing C++ rules:

union Thing
{
     short s;
     double d;

     Thing() : d(-7.65e75) { }
};

int main()
{
     Thing t;
     cout << t.s << '\n'; // prints garbage
     cout << t.d << '\n'; // prints "-7.65e+75"
}


When/if we extend the rules of C++ to allow user types in unions, one
restriction now is that a ctor for one of the contained objects must
be called.



>What happens if I refer to x.b.b after x is constructed?

In this case, you're screwed!  This is of course the case for existing unions:

union X { short s; double d };
X x;
int main() { cout << x.d << '\n'; } // don't expect to see 0.0


To use a union properly, we need an enum field outside the union that
says which field in the union is active.  If we have an array of unions
and each union has the same field active, we need only one enum field!


>I could probably see some logic behind allowing unions that contain
>only simple (built-in or POD) types to be default-initialized to
>all bytes zero.  But this could cause problems if a union contains
>pointer or floating-point members and all-bits-zero causes an
>exception for these types on some CPUs.

Existing unions can already contain float and pointer types.  As far
as I know, zero initializing any of these types should never cause
an exception.

The char[N] hack I posted about recently is a hack to get a union of
user defined types.  My preference is for the language to support it
directly.  One way for doing this is to relax the rules on the types
that are allowed in a union.  If type X has any of the following, it
can't be a member of a union:

(existing rule) any user ctor   ---   (new rule) user copy ctor
(existing rule) a   user dtor   ---   unchanged
(existing rule) a   user op=    ---   unchanged

union { X x; Y y; };

If the dtor is trivial, the compiler doesn't have to figure out which dtor
to call when the union goes out of scope.  It calls neither!  So we need
the rule on dtors as is.

If the copy ctor or op= is trivial, the compiler knows which copy ctor or
op= to call when copying the union.  It calls the copy ctor or op= for X
or Y, whichever has the most sizeof.  This is in fact what is done for
current unions; suppose X is char and Y is long -- copying the union
copies sizeof(long) bytes.  So we need the rule on copy ctors and op= as
is.

If we have other user defined ctors (ie, initialization ctors) in class X,
then current C++ forbids their use in unions.  But I think they would be
safe to use in unions.  It is clear which default ctor should be called
for the union if the union doesn't supply its own default ctor.  Call the
default ctor of the first element in the union.  We have a precedent for
this.  If the instanced union is at the class static, namespace, global,
or function static level, its first member is default initialized.  So
we'll do the same for user types in unions!


The restriction that the copy ctor, op=, and dtor of a union member be
trivial is essentially a restriction that the member be POD like.  Eg,
the following member would not be allowed in a union even though it's
copy ctor, dtor, and op= is implicitly generated by the compiler.  This
is because the corresponding funcs for contained object are are
non-trivial.  So, for example, it is not be clear which dtor we should
call when the union goes out of scope.

struct Impossible { std::vector<int> v; };

union { int i; Impossible impossible; };


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/10/29
Raw View
Siemel Naran wrote:
>
> [ From comp.lang.c++ ]
...
>
> So my question is, why isn't this allowed?  See below for an example:
>
> struct A { int    a; A(int    a_=0) : a(a_) { } };
> struct B { double b; B(double b_=1) : b(b_) { } };
>
> union X { A a; B b; }; // sizeof(X)==max(sizeof(A),sizeof(B))
>
> int main()
> {
>      X x; // calls X::X() which in turn calls A::A()
>      X y(x); // copies sizeof(X) bytes from 'x' to 'y'
> } // calls x.~X() and y.~X() which of course do nothing

Why does the definition of x invoke A::A() but not B::B()?
What happens if I refer to x.b.b after x is constructed?

I could probably see some logic behind allowing unions that contain
only simple (built-in or POD) types to be default-initialized to
all bytes zero.  But this could cause problems if a union contains
pointer or floating-point members and all-bits-zero causes an
exception for these types on some CPUs.

-- David R. Tribble, dtribble@technologist.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1998/10/26
Raw View
[ From comp.lang.c++ ]

On 25 Oct 1998 22:34:31 GMT, Santosh Baboo <santosh@issoln.com> wrote:

>Why is it that a union member is not allowed to have a constructor ??

I know why you can't have a member with a user defined copy ctor or a
user defined dtor in the union.  When you copy the union, the union
won't know which copy ctor to call -- should it copy member A using
A's copy ctor, or should it copy member B using B's copy ctor?  And
when the union goes out of scope, which dtor should be called?  As for
why the members of a union are not allowed to have default ctors is
beyond me.

-----

The problem is this.  We very often write value types and we may on
occassion want these value types to be members of unions.  Value
types are generally small objects, like currencies, iterators, complex
numbers, points, and so on.  These types don't manage dynamic memory,
are non-virtual (no virtual funcs in them), and have no virtual bases.
They also have trivial copy ctors and trivial dtors.  The dtor of class
X is trivial if it is implicitly generated by the compiler and if the
dtors for member variables and bases of class X are trivial.
Types with trivial copy ctors and trivial dtors would be safe in unions
as the union could ignore these and use its own.

So my question is, why isn't this allowed?  See below for an example:

struct A { int    a; A(int    a_=0) : a(a_) { } };
struct B { double b; B(double b_=1) : b(b_) { } };

union X { A a; B b; }; // sizeof(X)==max(sizeof(A),sizeof(B))

int main()
{
     X x; // calls X::X() which in turn calls A::A()
     X y(x); // copies sizeof(X) bytes from 'x' to 'y'
} // calls x.~X() and y.~X() which of course do nothing


And of course, we can have ctors inside X too.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]