Path: utzoo!utgpu!watmath!att!pacbell!ames!lll-winken!csd4.milw.wisc.edu!leah!rpi!nyser!cmx!dl From: dl@cmx.npac.syr.edu (Doug Lea) Newsgroups: comp.lang.c++ Subject: named return values Message-ID: <1826@cmx.npac.syr.edu> Date: 7 Aug 89 19:16:41 GMT Reply-To: dl@oswego.edu (Doug Lea) Organization: National Parallel Architectures Center Lines: 254 Organization: This is an argument for the `official' adoption in C++ of the notion of named return values, as have been present in GNU g++ for the last few versions. Named return values are a g++ extension that gives a name to the object returned by a function, in a way vaguely similar to named return `variables' in Pascal and other languages, but with some important C++-specific benefits. To allow for some examples and discussion, first declare a very simple vector-like class X: static const int Xsize=100000; // constant size, just for simplicity class X { int p[Xsize]; // Xs are BIG objects void copy(const X& a) { for (int i=0; i' is illegal. (Note: (4) is open for negotiation. It is arguable that a return should be legal, meaning to perform re-intialization or assignment to the already-initialized return value. However, this seems inelegant, error-prone, and substantially less simple than just disallowing it.) The most practical reason for desiring named return values is efficiency. Named return values allow one to write a function returning some large object without also first allocating space for and creating a local version that is to be somehow built-up solely for the sake of returning via a copy (i.e., using X(X&)). In the example, function iota declares local X y, which is allocated, constructed, modified, and then returned by value (via X(X&)) to the caller, whereas function iota_nrv directly creates and modifies the object being sent back to the caller, saving a time-consuming X(X&) constructor, and also saving the space otherwise needed to hold two Xs (during X(X&) construction), rather than just one. Thus, function iota_nrv is about twice as fast as function iota. The difference can, of course, be made arbitrarily dramatic by increasing Xsize. The best counterargument against this efficiency claim is that a *very* smart compiler could translate function iota into something like function iota_nrv all by itself. While conceivably true, in cases of arbitrarily complicated nesting of arbitrarily complicated functions, eliminating all such useless copying would sometimes require interprocedural data flow analyses stretching the capabilities of any optimizing compiler for any language I know. Thus, relying on automated detection seems unrealistic, especially considering that this effect can be had in such a simple and natural way with named return values. The second reason is in terms of C++ design goals and semantics, at least as I perceive them. [Please forgive the grandiosity of this apparent digression!] In a language like C++, which supports, to varying extents, object-oriented, procedural, and functional programming styles, one can think of objects (and their member variables and methods) as being central, and procedures and functions as being constructs that use and extend object capabilities in specific ways. To illustrate, assume you need procedure setv, a special-purpose routine that modifies some chunk of an X. void setv(X& x, int from, int to, int val) { for (int i = from; i <= to; ++i) x.set(i, val); } But there is often no reason that setv *must* exist as a stand-alone procedure. One could obtain the same kind of utility, albeit more awkwardly, by staying within a pure object-oriented framework and creating a new subclass that incorporates setv: class X_with_setv : public X { public: X_with_setv() :X() {} X_with_setv(int v) :X(v) {} void setv(int from, int to, int val) { for (int i = from; i <= to; ++i) set(i, val); } }; (The constructors are needed to ensure everything inherits transparently.) In some `pure' object-oriented languages, you might have to do something like this, but in C++, you can just write setv as a stand-alone procedure (as before) without all of this fuss, and with various other advantages. Enough warm-up. Now consider how one would similarly create a subclass incorporating function iota if one had to: class Xiota : public X { public: Xiota() :X() { for (int i=0; i' for `:'. (Compare rules (1-4) above with similar rules for constructors). If it weren't for the probable error-proneness resulting from the introduction of a different syntactic form for `:', (i.e., following it by a declaration for functions, but base initializers for constructors), using the colon instead of the return keyword for named return values, as in `X f() :x { ... }' might be a more logical choice. But the `return' syntax seems natural enough, and has some precedent in other languages. The only disadvantage I know of is that there is no programmer-defined control over when the constructor for the named return value is called. It must be called before any function body statements. But even this has a compensating advantage: It is impossible to write a function using named return values that `forgets' to return anything at all on one or more of its execution paths. (Although, of course, it is still possible to mistakenly write one that doesn't return what you had in mind!) There are still plenty of cases in which the old function syntax is still useful, often for logistics reasons: X f(int v) // no way to generate named ret val via a single expression { extern void g(int&); g(v); return X(v); } This example was also designed to show a construct, `return X(...)' (rather than a return of some local) that is compiled (probably by all C++ compilers) to be just as efficient as named return values since the constructor is in a tail call. This is among the very few cases where this is true. Its use is especially encouraged for those using C++ compilers lacking named return values. Support? Disagreement? Comments? Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2367 email: dl@oswego.edu or dl%oswego.edu@nisc.nyser.net UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl