Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!rutgers!apple!bbn!rochester!uhura.cc.rochester.edu!sunybcs!oswego!rocky.oswego.edu!dl From: dl@rocky.oswego.edu (Doug Lea) Newsgroups: comp.lang.c++ Subject: Re: C++ constructor and function semantics Message-ID: <1149@oswego.Oswego.EDU> Date: 15 Mar 89 11:38:13 GMT References: <1105@oswego.Oswego.EDU> <11310@ulysses.homer.nj.att.com> Sender: news@oswego.Oswego.EDU Reply-To: dl@rocky.oswego.edu.UUCP (Doug Lea) Organization: SUNY, Collego at Oswego Lines: 104 Jerry Schwarz replies to my posting on constructor semantics: >>The answer here is (and should be) that either behavior is correct. >>What is happening is that in one case the compiler has decided to put >>the return value into a temporary before constructing "var" from that >>temporary and in the other case it has decided not to. OK, even though I strongly object to the implication that temporary creation during object construction is nondeterministic, let's take it from there to get the same conclusions: * In forms like X var(fun()) (among other constructs) compilers may optionally evaluate functions into temporaries. * Such evaluation results in the insertion of nested X(X&) constructors. * The behavior of a correct C++ program cannot depend on whether such temporaries are created or not. * Hence, X(X&) constructors must be idempotent (thus also `pure'). * Hence, (among other things) * Any implicit or explicit form of X(X(...X(a)...)) may be elided into just X(a). * Programmers ought be able to assist in this effort via named return values. * Establishing an alternative `volatile' semantics deserves serious consideration. I should elaborate on a few understressed points in my original posting: Assuming the idempotency rule, I am not at all convinced that compilers should *enforce* either purity or idempotency in X(X&) constructors. As I mentioned, it is probably desirable to allow programmers to write code containing side effects with potential, but not actual computational consequences especially with respect to correct performance across nested X(X&)'s. I would, however like to see the `const X(const X&)' syntax supported, so that smart compilers would be allowed to assist programmers in ascertaining the purity of constructors, which would be very helpful for those attempting to verify the correctness of class behavior. In any case, programmers must be made aware that C++ compilers do implicitly assume idempotency. Someone correctly pointed out that this issue is not unrelated to the fact that side effects within functions are *always* risky in C++ and most other languages. For example in `int a = func1() + func2()', since order of evaluation of operands is undefined, if `func1' contains a side effect affecting `func2', and vice versa, then the result of the expression is ill-defined. Language definitions for C, C++, etc., merely warn programmers that such constructs are to be avoided, without otherwise outlawing side effects. The two differences between this and the X(X&) issue are that (1) this is not at all the same kind of nondeterminism: X(X&) constructors are always `inserted' in *nested* fashion X(X(...X(a)...), so there is no ambiguity about evaluation order, assuming conformity to the innermost-first `applicative' function evaluation rule central to any Algol-type language, and (2) programmers *cannot* avoid nested X(X&) constructors since they are inserted and/or elided automatically by compilers. Thus, again, my main conclusions hold. I should also have mentioned that the named return value construct saves not only an X(X&), but also the corresponding ~X() destructor. Jerry continues, in discussing storage management techniques based on constructor sequences, >>that a constructor will be called for any space it uses as an X (e.g. >>the space used for function arguments) it is possible to write such >>code so that it is correct whatever sequences are actually invoked. >>Although the efficiency of the resulting code may be lowered if the >>C++ compiler chooses to create a lot of temporaries. Right. Try explaining this to applications programmers who find that that their 1000 X 1000 element matrices are being copied two or three times on their way out of functions! (or that `a = b + c + d;' for matrices requires more data copying than logically necessary, or...) Like I said, there *are* good ways to deal with such problems, but some of the best ways require a better specification of function and constructor semantics than C++ and/or its implementations currently provide. Semantic precision is not just a `nice' attribute of a language -- It enables programmers to write better code! (Along these lines, I wonder just how many existing C++ programs will `break' if/when cfront and g++ make `X var(fun())' operationally equivalent to `X var = fun()'.) Readers of this newsgroup may expect a few additional postings on some further language clarifications and the like that I think are necessary (or at least desirable) in order to better fulfill Stroustrup's (and my and probably your) hopes that C++ really can serve as a basis for creating special-purpose value-oriented classes that are as natural, efficient, and correct as possible in any language. I hope these things are accepted not as ultimatums or whinings, but as earnest attempts to provide useful and friendly feedback. Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2367 email: dl@rocky.oswego.edu or dl%rocky.oswego.edu@nisc.nyser.net UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl