ISO: WG21/N0500 ANSI: 94-0113 Author: John Max Skaller Date: May 1994 Reply to: maxtal@suphys.physics.su.oz.au "ENHANCED UNIONS" ----------------- 1) This paper suggests that the ARM, 9.5 Unions, and the pre-San Diego WP, 9.6/1 [class.union] Unions, which say, and I quote: "A union may be thought of as a structure whose member objects all begin at offset zero and whose size is sufficient to contain any of its member objects." should be interpreted literally as the defining property of a union. PROPOSAL 1. ----------- The whole of 9.6/1 is replaced by: "A union is a struct whose non-static members all begin at offset zero such that the sizeof the struct is not less than the sizeof any of these members; a union: union U { // other than non-static data members // non-static data members } is equivalent to struct U { // other than non-static data members union { // non-static data members }; } which defines unions in terms of anonymous unions." This substitution removes all the restrictions on unions given in sentences after the first one of [class.union]/1. The editorial box 48 (pre-San Diego) should be removed. ------------------------------------------------------------------------- 2) We proceed to clarify the rules for anonymous unions. Rules [class.union]/2, 4 and 5 remain intact. PROPOSAL 2 ---------- Add to [class.union]/3 the clause: "An anonymous union may not have static members." Add: [class.union]/6 User constructors. ---------------------------------- A user written constructor of a class containing an anonymous union shall not explicitly specify a mem-initialiser for more than one non-static data member of that anonymous union; no member of an anonymous union shall be initialised unless a mem-initialiser is explicitly written. For example: union U { T1 t1; T2 t2; U(T1 const& p1) : t1(p1) {} U(T2 const& p2) : t1(p2) {} U(U const& pu) : t1(pu.t1), t2(pu.t2) {} // error }; [class.union]/7 Generated destructor. ------------------------------------- A compiler generated destructor of a class containing an anonymous union shall not destroy any member of that anonymous union; user written destructors do not implicitly destroy any member of an anonymous union. [class.union]/8 Generated copy constructor and copy assignment operator. ------------------------------------------ A compiler generated copy constructor or copy assignment of a class containing an anonymous union shall perform a bitwise copy of the non-static storage extent of the anonymous union. If any of the members of the anonymous union is not bitwise copyable[ref], the compiler shall not generate a copy constructor or copy assignment operator. [class.union]/9 Generated default constructor. ---------------------------------------------- A generated default constructor of a class containing an anonymous union shall not initialise any members of the anonymous union. Incorporate the following somewhere. ------------------------------------ Definition: Bitwise copyable type --------------------------------- Each of an enum type, pointer to member, pointer, bool type, or arithmetic type is bitwise copyable(assignable), references are not bitwise copyable. A struct is bitwise copyable(assignable) if, and only if, each non-static data member is bitwise copyable(assignable), and provided the user does not supply a copy constructor (copy assignment operator). An anonymous union is bitwise copyable if the corresponding struct is. Specification: Copying bitwise copyable types ---------------------------------------------- If a copy constructor (copy assignment operator) is generated by the compiler for a bitwise copyable type T,then the copy constructor (assignment operator) shall be functionally equivalent to a memcpy() of sizeof(T) bytes. Specification: Copying bitwise anonymous unions ---------------------------------------------- If a copy constructor (copy assignment operator) is generated by the compiler for a bitwise copyable anonymous union, then the copy constructor (assignment operator) shall be functionally equivalent to a memcpy() of sizeof the unnamed object. -------------------------------------------------------------------------- RATIONALE: Problem and Resolution summary ----------------------------------------- P1) Unions have special rules which introduce complexity to the C++ language and the Working Paper. R1) Unions are classes defined in terms of anonymous unions, the complexity is delegated to anonymous unions. P2) Unions have restrictions on declarations which restrict programmers. R2) All restrictions are removed by treating unions as classes. P3) In particular, C++ has no safe discriminated unions, and the restrictions on ordinary unions make idiomatic emulation of discriminated unions quite difficult. R3) The restrictions are lifted and a sample idiom for a discriminated union given below as an example. P4) Its not clear even for ordinary unions if user written constructors may initialise more than one non-static data member with explicit mem-initialisers. R4) Delegated to anonymous unions. P5) Its not clear for anonymous unions in structs if user written constructors may initialise more than one non-static data "member" with explicit mem-initialisers. R5) Precise rules are given. P6) It is not clear if unions may contain references. R6) They may just as a class may, so too may an anonymous union. --------------------------------------------------------------------------- Comparison with classes ----------------------- CLASSES ENHANCED UNIONS ARM UNION ------------------------------------------------------------------------- User constructor: Mem-initialiser .. Not written Default init No init No init Explictly written At most one At most one Not specified per member member Generated non-copy constructor: Default init Yes No No Generated copy constructor: Memberwise Bitwise or error Bitwise** Generated copy assignment: Memberwise Bitwise or error Bitwise** Generated destructor: Destroy member automatically Yes No No** Brace initialise: If aggegate First member First member if all members (all members are aggregates must be aggregates) Allow constructible member: Yes Yes No Can have bases, virtuals Yes Yes No Can be base Yes Yes No ** The requirements for ARM and Enhanced unions are the same, the difference is that ARM unions diagnose errors at the point of declaration even if no member function need be generated; whereas enhanced unions defer error diagnoses until the point of use. ------------------------------------------------------------------------ EXAMPLE: Safe disriminated union idiom -------------------------------------- The following code provides a "safe" disciminated union of two types A and B. It permits an "empty" union. Invalid accesses are trapped at run time. // tag wrapper class struct abtag { enum abcode {empty, A_t, B_t} tag; abtag(abcode x=empty) : tag(x) {} }; union AB : private abtag { private: A a; B b; public: // default empty union AB() : abtag(empty), b(bb) {} // individual type constructors AB(A const& aa) : abtag(A_t), a(aa) {} AB(B const& bb) : abtag(B_t), b(bb) {} // copy constructor AB(AB const& ab) : abtag(ab.tag) { switch(tag) { case A_t: new(&a) A(ab.a); break; case B_t: new(&b) B(ab.b); break; } } // real destructor code void destroy() // empty the union { switch(tag) { case A_t: a.~A(); break; case B_t: b.~B(); break; } tag=empty; } // destructor ~AB(){ destroy(); } // copy assignment void operator=(AB const& ab) { if(this != &ab) { destroy(); new(this) AB(ab); } } // allow user to get the typecode abcode getType()const { return tag; } // provided checked references access A& refA() { if(tag!=A_t) throw ("wrong type"); return a; } B& refB() { if(tag!=B_t) throw ("wrong type"); return b; } A const& refA()const { if(tag!=A_t) throw ("wrong type"); return a; } B const& refB()const { if(tag!=B_t) throw ("wrong type"); return b; } // provide checked pointer access: return 0 for wrong type A * ptrA(){ return tag==A_t ? &a : 0; } B * ptrB(){ return tag==B_t ? &b : 0; } A const* ptrA()const { return tag==A_t ? &a : 0; } B const* ptrB()const { return tag==B_t ? &b : 0; } };