[comp.lang.c++] unions and classes with constructors

akwright@watdragon.waterloo.edu (Andrew K. Wright) (02/15/89)

Though this may appear at first to be yet another posting about constructors,
I believe this issue is new, or at least has not appeared for some time.

I would like to take issue with the rule that classes with constructors
with constructors may not appear as elements of a union.  This rule is
mentioned in Bjarne's book, and is enforced by cfront 1.2

The rule seems to me to be overly restrictive.  It is clear why it is
needed:  since unions have no tag, the compiler cannot possibly determine,
even at runtime, which element of the union a constructor/destructor
should be called.  BUT, there are cases when it is not important.  If
no destructor is defined for a class, and if the constructor for a class
does not take over storage allocation for the class, then it seems reasonable
to allow the user to place such a class in a union.  So long as the user
calls the constructor explicitly for the field (s)he wishes to use,
no problem occurs.  This is analogous to remembering to intialize
locals before you use them.  You must initialize a union field before
you use it.

An example of where this is desirable is in using YACC with C++.  Currently
you are prevented from using a class with a constructor in YACC's %union
construction, because YACC's %union is implemented with a C(++) union.
In fact, YACC type checks all uses of $ variables, so YACC's %union
is type safe.  Thus the restriction that C++ imposes seems unnecessary
here, IF your class has no destructor and does not take over storage allocation.
eg.
	class Complex {
		double re, im;
		Complex( double r, double i ) { re = r; im = i; }
	}
With the above simple class, there seems no reason not to allow it
inside a union.

Any chance this might be addressed in upcoming releases of C++?
How do other people deal with the YACC issue mentioned above?  (I have
  several potential solutions, none of which I like).

Andrew K. Wright      akwright@watmath.waterloo.edu
CS Dept., University of Waterloo, Ont., Canada.

dld@F.GP.CS.CMU.EDU (David Detlefs) (02/15/89)

<< Andrew Wright points out that there are cases in which the
   prohibition against having objects of classes with constructors as
   members of unions is inconvenient; the YACC %union construct is
   such a case.  He proposes removing this restriction for certain
   cases. >>

I don't think this is a great idea, because it invalidates an
invariant that C++ goes to great lengths to protect: if a class has a
constructor, then you can assume that *any* object of that class has
had a constructor called before you get to see it.  I find this very
comforting, and would hate to see it have "except for's" put in it.

However, I agree that not being able to put class objects in
union-type things is a problem.  And I agree with the spirit of your
solution.  Let me cast in another light: add a tagged union construct,
like the "variant" of CLU (and, I believe, Mesa.)  The project I'm
working on here at CMU is called Avalon/C++, and extends C++ to
support transaction-based programmings and persistent objects.  One of
the extensions Avalon/C++ adds is such a variant construct (Stewart
Clamen was the main implementor of this.)  The declaration

variant foo {
  int x;
  char* str;
  bar b;
};

declares a variant that can be in one of four states x, str, b, or the
special void state.  Foo can be thought of as a class with a null
constructor, which initializes the variant object to be in the void
state.  The above declaration declares member functions

  bool is_void();
  bool is_x();
  bool is_str();
  bool is_b();

  void set_void();
  void set_x(int);
  void set_str(char*);
  void set_b(bar&)

  int value_x();
  char* value_str();
  bar& value_b();

A foo object is initially in the void state; you use a set_???
operation to put it in a state, and the is_??? and value_???
operations to manipulate it.  We are considering whether it would be
worthwhile to add a special control structure to manipulate variants
(like the CLU "tagcase") to the language.

Unlike unions, variants could be used in a type-safe way.  I would
claim that there are just about no legitimate uses for untagged unions
that tagged unions would not serve just as well, in a more error-free
way.  Also, if a program uses variants instead of unions, various
other benefits accrue.  For example, since all types would then be
run-time determinable, it would be possible for a compiler to
automatically add general procedures for dumping objects to files and
reading them back.

My own feeling is that an alternative to unions and lack of
parameterized types are the two main things that keep C++ from being
as good as any programming language there is.  I'm happy that work is
proceeding on parameterized types; perhaps there will eventually be an
extension along these lines someday.  I don't think it breaks
anything; the major criticism is that it overlaps to closely with the
function of "union."  I think the extra safety is worth it.  (BTW,
I'd really like to see some sort of empirical data on how many of the
errors that occur in real C programs would be preventable by
different language decisions -- like garbage collection, variants,
assignment as a statement instead of an expression to eliminate =/==
bugs, etc.  If anyone has a reference for such a study, please let me
know.)





--
Dave Detlefs			Any correlation between my employer's opinion
Carnegie-Mellon CS		and my own is statistical rather than causal,
dld@cs.cmu.edu			except in those cases where I have helped to
				form my employer's opinion.  (Null disclaimer.)
-- 

nevin1@ihlpb.ATT.COM (Liber) (02/16/89)

In article <11560@watdragon.waterloo.edu> akwright@watdragon.waterloo.edu (Andrew K. Wright) writes:
>I would like to take issue with the rule that classes with constructors
>with constructors may not appear as elements of a union.

>So long as the user
>calls the constructor explicitly for the field (s)he wishes to use,
>no problem occurs.  This is analogous to remembering to intialize
>locals before you use them.  You must initialize a union field before
>you use it.

But this blows my abstract notion of what a class is.  To me, a class
is, among other things, an interface.  A user of a class should not
have to worry about the details of the implementation of a class.
Calling a constructor explicitly requires me, from the user's point of
view, to understand something about how classes are implemented.  You
should not have to go beneath the abstract level of "new" and "delete"
when using a class.  [Note:  by user, I don't include inheritance of
one class into another class.]

>Thus the restriction that C++ imposes seems unnecessary
>here, IF your class has no destructor and does not take over storage allocation.

This is where the real problem is.  This not only blows the
abstraction, but classes could no longer be clean interfaces.  The user
of a class should not be dependent on the implementation of a class.
With your proposal, the use of the class in a union would be entirely
dependent on whether or not I have a destructor for that class.  The
usage of the class would always be tied to the implementation; why have
a class at all?

>eg.
>	class Complex {
>		double re, im;
>		Complex( double r, double i ) { re = r; im = i; }
>	}
>With the above simple class, there seems no reason not to allow it
>inside a union.

I would not be as free to reimplement Complex in any way that I choose.
One of the advantages of C++ is that I can reimplement a class and not have
to touch any of the code using the class; your proposal would violate
this.
-- 
 _ __	NEVIN ":-)" LIBER  nevin1@ihlpb.ATT.COM  (312) 979-4751  IH 4F-410
' )  )			 "I will not be pushed, filed, stamped, indexed,
 /  / _ , __o  ____	  briefed, debriefed or numbered!  My life is my own!"
/  (_</_\/ <__/ / <_	As far as I know, these are NOT the opinions of AT&T.

akwright@watdragon.waterloo.edu (Andrew K. Wright) (02/18/89)

In article <9595@ihlpb.ATT.COM> nevin1@ihlpb.UUCP (55528-Liber,N.J.) writes:
>In article <11560@watdragon.waterloo.edu> akwright@watdragon.waterloo.edu (Andrew K. Wright) writes:
>>I would like to take issue with the rule that classes with constructors
>>with constructors may not appear as elements of a union.
>
>>So long as the user
>>calls the constructor explicitly for the field (s)he wishes to use,
>>no problem occurs.  This is analogous to remembering to intialize
>>locals before you use them.  You must initialize a union field before
>>you use it.
>
>But this blows my abstract notion of what a class is.  To me, a class
>is, among other things, an interface.  A user of a class should not
>have to worry about the details of the implementation of a class.
>Calling a constructor explicitly requires me, from the user's point of
>view, to understand something about how classes are implemented.  You
>should not have to go beneath the abstract level of "new" and "delete"
>when using a class.  [Note:  by user, I don't include inheritance of
>one class into another class.]

The abstract notion of what a class is remains unchanged.  It is the
union construct, not the class notion, that is causing the problem here.
When you use a C union, all bets are off.  Because the union is untyped,
you must be careful to use the union object correctly.  The rule stating
"classes with constructors cannot appear in a union" is an attempt
to provide some measure of safety.  But my point is that so long as
one keeps the view that in using a union the programmer must be careful,
this rule is overly restrictive.  This is not really any different
than unions in simple C.  Although the rule appears to provide some
protection for the class abstraction, there are plenty of other ways
I can shoot holes in a class abstraction, so why bother to close
this one so absolutely?

The user of a union must be careful.  If your union contains a class
with a constructor, you must be careful to call the constructor when
using that field (and destructor when done).  If your union contains
only simple types, int or float say, you must still be equally careful
not to use a garbage value.  I see no real difference.

Andrew K. Wright      akwright@watmath.waterloo.edu
CS Dept., University of Waterloo, Ont., Canada.