Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!YAHI.STANFORD.EDU!tiemann From: tiemann@YAHI.STANFORD.EDU (Michael Tiemann) Newsgroups: gnu.g++ Subject: Generating temporaries Message-ID: <8904042239.AA07890@yahi.stanford.edu> Date: 4 Apr 89 22:39:52 GMT References: <9431@claris.com> Sender: daemon@tut.cis.ohio-state.edu Reply-To: tiemann@lurch.stanford.edu Distribution: gnu Organization: GNUs Not Usenet Lines: 123 I hope I am not stealing Andy Koenig's fire in answering a C++ langauge question... >From article <7.UUL1.2#261@persoft.UUCP>, by ericf@persoft.UUCP (Eric R. Feigenson): > [I'm rather new to C++, so this may be a rather naive query. Please bear > with me] Nope, good question. > I have a class M for which I want to define operator+. My question(s) have > to do with how I generate the temporary that contains the result, and how > such a temporary gets destroyed. > > First, if my operator+ returns an object of class M, it has to create > an object to return. If I understand things right, this will call > operator+, which will return an object of class M (which was > pushed onto the stack), and do a structure copy into c. All the destructors > get called just fine, and no stray memory is left around, but there's the > overhead of the structure copy. Well, it all depends on the compiler you are using. I know that at least AT&T cfront and GNU C++ are smarter than this. What happens in these compilers is that the caller passes the address of the place where the new temporary should be initialized. Depending on the way it is initialized, there may be no overhead visible from the call to operator+ at all: M operator+(M x, M y) { return M (x.value () + y.value ()); } In general, if the compiler can get away with generating an invisible temporary, it can do quite clever things. But you may need to do hairier things, which require you to get a handle on the value you are returning: M operator+(M x, M y) { M tmp; // call to default constructor, if any. tmp.set (x.value () + y.value ()) if (fullmoon ()) tmp.howl (); return tmp; // call to M(M&) constructor, or structure copy, sigh } A really smart compiler could notice that tmp was the only variable feeding the return value, and substitute the hidden result parameter for tmp throughout. Dumber compilers can get around this problem by extending the language: M operator+(M x, M y) return tmp; // call to default constructor { tmp.set (x.value () + y.value ()) if (fullmoon ()) tmp.howl (); return tmp; // this need do nothing, since we initialized the // return value in place } Now here's not what you want to do in any event: > Now I've also fiddled around with trying to return a reference to an object > M, in order to avoid the structure copy. The code for operator+() has to > allocate a pointer to an object of type M using the "new" operator. > So, if we have some code that looks like: > > M a, b, c; > > // some code to give a and b values > > c = a + b; // calls "M& M::operator+(M& x)" > > and we define operator=(M&) to do the assignment, how can we free the > space allocated by operator+() for the temporary result? If operator=() > does the "delete" then it may destroy some space that we meant to keep > around. Also, in an expression such as "a + b + c", how can the > intermediate temporaries be freed? The destructors get called, but how > can they know to delete the space on the free store? The answer to these questions is: if you do this, you won't win. What you should do is try to use C++ in the way it was intended to be used. It is your programs you should try to outsmart, not the compiler. As a general rule of thumb, you can safely assume that no construct was added to C++ which would not have good efficiency. Return-by-value is no exception. > > To summarize: I want to be able to define operator+ (or any other operator > for that matter) using references (pointers) to avoid the overhead of > structure copy. The problem seems to be knowing when and how to free the > space that the operator must dynamically allocate in order to generate a > result. > > I hope this makes sense. Thanks in advance for your help. Please e-mail > any responses. Hope this helps. Michael I think this is a good question whose answer should be posted rather than just emailed. PS: this is another thing to just not do: One thing you can do is have operator+ have a static local object of class M that it puts the result into. You can return a reference to this, and after you do the copy a = b + c to a from the temp the temp is not referenced anymore, so it is free to be used again whenever it is needed. You don't have to use new or delete, and in fact the storage for the temp never has to get thrown away. This makes the program non-reentrant. Your solution is analogous to the way that the AT&T C compiler does structure returns. This has long been considered one of the great implementation botches of that compiler. Bob Hearn hearn@claris.com Michael