Path: utzoo!mnetor!tmsoft!torsqnt!news-server.csri.toronto.edu!cs.utexas.edu!usc!apple!portal!fernwood!lia!jgro From: jgro@lia (Jeremy Grodberg) Newsgroups: comp.lang.c++ Subject: Re: Default copy constructor not making a copy. Message-ID: <1991Feb19.030423.2454@lia> Date: 19 Feb 91 03:04:23 GMT References: <22023@hydra.gatech.EDU> Reply-To: jgro@lia.com (Jeremy Grodberg) Lines: 91 In article <22023@hydra.gatech.EDU> gatech.edu!blsouth!klein (Michael Klein) writes: >Can someone explain why the two classes defined below exhibit different >behavior? The class without the explicit copy constructor does not make >a copy of the *this argument for the + operator.... > >#include >#include > >class NoCopy { >public: > int x; > NoCopy (int a) { x = a; }; > NoCopy &operator +=(int a) { x += a; return *this; }; > NoCopy operator + (int a) { return NoCopy(*this) += a; }; >}; The ARM is immensly confusing on this issue, and I was going to post saying that it was completely ambiguous whether NoCopy(*this) should return a distinct object or not. My thought was that if NoCopy(*this) is treated as a cast, then it was OK for NoCopy(*this) to evaluate to the same object you started with. However, after a lot of digging, I believe I can make the case that NoCopy(*this) should return a distinct object in this situation. The example output shows that NoCopy(*this) is in fact returning *this, and not a temporary object. The problem seems to be the ambiguity between NoCopy(*this) as a cast operator (Sec. 5.2.3) and as an explicit constructor call (Sec. 12.1). Section 6.8 attempts to disambiguate this expression, and claims it is an expression statement, given a choice between and expression and a declaration, but Section 12.1 makes no comment about the ambiguity between a cast and an explicit constructor call. It appears that there is no difference. Section 5.2.3 says that a function-style cast "constructs a value of the specified type...." Section 5.4 makes no distinction between a function-style cast and a normal C style cast except that the latter can be used to convert to a type that does not have a "simple-type-name." It goes on to say that "An object or a value may be converted to a class object (only) if an appropriate constructor or conversion operator has been defined," which implies that such constructor or conversion operator will be called. Therefore, it appears the only time there is a difference between a cast and an explicit constructor call is when the "destination" of a cast is not a class object, in which case the expression cannot possibly be interpreted as a constructor call. Therefore, the ARM at least strongly implies that whether this NoCopy(*this) is treated as a function-style cast or as and explicit constructor call, it should return a distinct object. Another reason to expect a distinct object is that there is no alternate syntax for creating an unnamed temporary. So, I'd say we have a bug in CFront, and some fuzzy areas in the spec to clean up. If you say you can make the case that I am wrong, then I will go back to my original position that the ARM is too unclear about this to say either way, and we really need a better spec (and an answer from the C++ gods in the interim). > >class Copy { >public: > int x; > Copy(const Copy &c) : x(c.x) {}; > Copy (int a) { x = a; }; > Copy &operator =(int a) { x = a; return *this; }; > Copy &operator +=(int a) { x += a; return *this; }; > Copy operator + (int a) { return Copy(*this) += a; }; >}; Here, Copy(*this) is taken as an explicit constructor call, and you get a different, temporary object. > >void main() >{ > NoCopy xNo(1), xNo2(-1); > Copy xYes(2), xYes2(-2); > cout << "No=" << xNo.x << " Yes=" << xYes.x << endl; > cout << "No2=" << xNo2.x << " Yes=" << xYes2.x << endl; > xNo2 = xNo + 2; > xYes2 = xYes + 2; > cout << "No=" << xNo.x << " Yes=" << xYes.x << endl; > cout << "No2=" << xNo2.x << " Yes=" << xYes2.x << endl; >} > >Output of the program: >No=1 Yes=2 >No2=-1 Yes=-2 >No=3 Yes=2 >No2=3 Yes=4 -- Jeremy Grodberg "I don't feel witty today. Don't bug me." jgro@lia.com