Topic: Initialization ambiguities


Author: jimc@julia.math.ucla.edu
Date: 10 Apr 91 19:02:11 GMT
Raw View
Two possible ambiguities about initialization in Ellis & Stroustrup,
"The Annotated C++ Reference Manual".  First, suppose class A is an
aggregate (sect 8.4.1) containing one data member, an array of V's,
which are also aggregates.
    struct V { float data[2]; }
    struct A { V array[2]; }
    A wrong = { {1, 2}, {3, 4} }; //{3,4} inits 2nd data member
     //which does not exist
    V v0 = {1, 2};
    V v1 = {3, 4};
    A ambig = { { v0, v1 } };  //What does v0 init?
  //Could be ambig.array[0], could be ambig.array[0].data[0]
When an aggregate is initialized from a list of aggregates, and when one
of these matches a sub-aggregate, is it copied or does it (disastrously)
initialize members of the sub-aggregate?  The book can be read either way.

What I really wanted was the effect shown above as "wrong", just as a
traditional "C" array would be initialized.

Second ambiguity: a derived class has a constructor from a reference to its
base class.  Is this a copy constructor?  Specifically:
    struct Base {
 Base(args);  //No explicit copy constructor here
    };
    struct Derv : public Base {
 Derv(const Base& b); //Pseudo-copy constructor
    };
    Derv& invert(const Base& b) {
 Derv result(b);
 /* thrash about */
 return result;  //Using which copy constructor?
    }
Reading in sect. 12.6.1 "Explicit Initialization" p. 288: "...function
return is equivalent to Derv dest = result".  p. 284: "This value is
used as the argument to a copy constructor".  Sect. 5.3.3 "New" p. 61:
"Access and  ambiguity control are done for both operator new() and the
constructor". It all implies very strongly that a constructor should be
sought that will swallow a Derv, which the provided constructor will
do.  Nonetheless my compiler demanded an exact match on argument type,
chucked my constructor, and generated a bitwise copy (whereupon on
destruction a free pointer was freed again, the heap was smeared,
MS-DOS was smeared, my disc was smeared, and matters went downhill from
there.  Fortunately I make backups.)

An authoritative statement would be appreciated in the evolving standard
of whether a copy constructor can have non-exact matching arguments.

If you post a reply, I would appreciate a mailed copy since my news machine
has a very short expiration time.

James F. Carter        (213) 825-2897
UCLA-Mathnet;  6221 MSA; 405 Hilgard Ave.; Los Angeles, CA, USA  90024-1555
Internet: jimc@math.ucla.edu            BITNET: jimc%math.ucla.edu@INTERBIT
UUCP:...!{ucsd,ames,ncar,gatech,purdue,rutgers,decvax,uunet}!math.ucla.edu!jimc
James F. Carter        (213) 825-2897
UCLA-Mathnet;  6221 MSA; 405 Hilgard Ave.; Los Angeles, CA, USA  90024-1555
Internet: jimc@math.ucla.edu            BITNET: jimc%math.ucla.edu@INTERBIT
UUCP:...!{ucsd,ames,ncar,gatech,purdue,rutgers,decvax,uunet}!math.ucla.edu!jimc




Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 19 Apr 91 17:42:42 GMT
Raw View
In article <1991Apr10.190211.29678@math.ucla.edu> jimc@MATH.UCLA.EDU (Jim Carter) writes:
|Second ambiguity: a derived class has a constructor from a reference to its
|base class.  Is this a copy constructor?  Specifically....
....my compiler demanded an exact match on argument type,
|chucked my constructor, and generated a bitwise copy....

I agree that ARM is less than 100% clear in its language on this issue,
but it seems to me the intent is pretty darned clear.  All the examples
given show copy constructors [and assignments] of a class X matching
an X& or a const X&.  For example, see ARM page 296 "If a class X has
any X::operator=() that takes an argument of class X, the default assignment
will not be generated.  I believe the authors [and most readers] didn't
recognize that there is a [slight] ambiguity in the text.  I think the
[almost] universally recognized intent is that a copy constructor must have
some kind of Dirv& parm, not a Base& parm.  If then, a programmer doesn't
explicitly lay down a copy constructor [or assignment op] [that is: one that
takes a Dirv& or const Dirv& parm] then a compiler must lay down a default
*memberwise* constructor [or assignment op.]  Which presumably is what
bit you.

Because of these kinds of problems, many old time C++ hacks just by habit
manually create copy constructors and assignment operators for each class
they create.  Otherwise it is easy to make changes to a class which defeats
the default constructors and assignments -- and one doesn't notice the
problem since one hasn't explicitly laid down code for the constructor and
assignment. [Also, this [IMHO] good habit of "always" explicitly laying down
constructors and assignment were reinforced by the flaky code generating
capabilities of many early C++ compilers]

In summary:  I agree that the ARM is less than 100% clear on this issue,
but I don't think there is any "emerging consensus" on the issue -- rather
I think its been clear for several years that copy constructors and assignment
require a parm of Dirv& or const Dirv&, that the generated copy constructors
and assignment work by *memberwise* assignment, not *bitwise* assignment,
and this is universally accepted in all recent compiler releases.

Perhaps the committee can make a point of cleaning up these slight ambiguities
in the ARM text?