Thread View: comp.std.c++
2 messages
2 total messages
Started by Dirk Becker
Sat, 10 Feb 1996 15:39
Re: class.union questions
Author: Dirk Becker
Date: Sat, 10 Feb 1996 15:39
Date: Sat, 10 Feb 1996 15:39
306 lines
12128 bytes
12128 bytes
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:
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.
So the following sample seems perfectly legal with the WP, not with ARM.
The commented lines are the trivialness-required special member functions.
class Bar {
public:
// Bar(); // trivial default constructor
// Bar(const Bar&); // trivial copy constructor
// ~Bar(); // trivial destructor
// Bar& operator = (const Bar&); // trivial copy assignment
// plain member function - no problem
Bar& operator_assign(const Bar&);
// some other constructors - I see no problem here
Bar(int);
Bar(float);
// several other simple assignments are allowed
Bar& operator = (int);
Bar& operator = (float);
// these are assignments too, see ARM 5.17 or [expr.ass]
Bar& operator += (const Bar&);
Bar& operator -= (const Bar&);
Bar& operator *= (const Bar&);
Bar& operator /= (const Bar&);
// ... data members etc.
};
union Foo {
Bar bar;
// ... others
};
DB> So you can have any special assignment operator or constructor for
DB> your member objects, any but the most common and useful ones.
JK> Correct. The reason is simple: assignment, copy, default construction
JK> and destruction of a union must be correctly defined without the
JK> compiler knowing which element the union actually contains. The set
JK> of forbidden operators are those that the compiler might be called
JK> upon to use in implicitly generated code.
But why would you like to provide your own constructor and assignments?
Maybe because you have added some side effects, like range checking or
trigger a reference counter.
If you only use arguments matching your custom routines, this will work
fine. But if you accidentally assign one Foo.bar to another, your
side-effects won't take place because the WP prevented your custom copy
assignment operator.
It is even impossible to hide the unwanted implicit constructors and
copy assignment into privacy, to make your compiler produce an error
message.
Foo foo1;
Foo foo2;
foo1.bar = 4711; // applies your side-effects
foo2.bar = foo1.bar; // just trivial assignment here due to WP
-----
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"
-----
/* 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.
-----
/* 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".
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()
{ }
-----
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 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"?
"The implicit union copy constructor and copy assignment apply
bitwise copy."
No trivialness restrictions on the member objects!
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.
-----
d)
DB> Why is there no anonymous struct?
JS> The committee (as a body) does not find arguments for
JS> orthogonal design appealing. It is hard to demonstrate
JS> why orthogonal design is good to those who have little
JS> experience with orthogonal systems -- most of us,
JS> considering the horrid systems we're forced to work with
JS> most of the time.
JK> Agreed here. Although I really don't see it as worth the bother.
JK> (I could do without anonymous unions, too.)
You won't need the anonymous struct, because you already said you
don't use unions.
-----
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.
------------------------------------------------------------------
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://reality.sgi.com/employees/austern_mti/std-c++/policy.html
]
Re: class.union questions
Author: J. Kanze
Date: Fri, 16 Feb 1996 17:15
Date: Fri, 16 Feb 1996 17:15
279 lines
12941 bytes
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. ]
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads