Go forward to Pix.
Go backward to Representations.
Go up to Top.

Some guidelines for using expression-oriented classes
*****************************************************

   The fact that C++ allows operators to be overloaded for user-defined
classes can make programming with library classes like `Integer',
`String', and so on very convenient. However, it is worth becoming
familiar with some of the inherent limitations and problems associated
with such operators.

   Many operators are *constructive*, i.e., create a new object based
on some function of some arguments. Sometimes the creation of such
objects is wasteful. Most library classes supporting expressions
contain facilities that help you avoid such waste.

   For example, for `Integer a, b, c; ...;  c = a + b + a;', the plus
operator is called to sum a and b, creating a new temporary object as
its result. This temporary is then added with a, creating another
temporary, which is finally copied into c, and the temporaries are then
deleted. In other words, this code might have an effect similar to
`Integer a, b, c; ...; Integer t1(a); t1 += b; Integer t2(t1); t2 += a;
c = t2;'.

   For small objects, simple operators, and/or non-time/space critical
programs, creation of temporaries is not a big problem. However, often,
when fine-tuning a program, it may be a good idea to rewrite such code
in a less pleasant, but more efficient manner.

   For builtin types like ints, and floats, C and C++ compilers already
know how to optimize such expressions to reduce the need for
temporaries. Unfortunately, this is not true for C++ user defined
types, for the simple (but very annoying, in this context) reason that
nothing at all is guaranteed about the semantics of overloaded operators
and their interrelations. For example, if the above expression just
involved ints, not Integers, a compiler might internally convert the
statement into something like ` c = a; c += b; c+= a; ', or perhaps
something even more clever.  But since C++ does not know that Integer
operator += has any relation to Integer operator +, A C++ compiler
cannot do this kind of expression optimization itself.

   In many cases, you can avoid construction of temporaries simply by
using the assignment versions of operators whenever possible, since
these versions create no temporaries. However, for maximum flexibility,
most classes provide a set of "embedded assembly code" procedures that
you can use to fully control time, space, and evaluation strategies.
Most of these procedures are "three-address" procedures that take two
`const' source arguments, and a destination argument. The procedures
perform the appropriate actions, placing the results in the destination
(which is may involve overwriting old contents). These procedures are
designed to be fast and robust. In particular, aliasing is always
handled correctly, so that, for example `add(x, x, x); ' is perfectly
OK. (The names of these procedures are listed along with the classes.)

   For example, suppose you had an Integer expression ` a = (b - a) *
-(d / c); '

   This would be compiled as if it were ` Integer t1=b-a; Integer
t2=d/c; Integer t3=-t2; Integer t4=t1*t3; a=t4;'

   But, with some manual cleverness, you might yourself some up with `
sub(a, b, a); mul(a, d, a); div(a, c, a); '

   A related phenomenon occurs when creating your own constructive
functions returning instances of such types. Suppose you wanted to
write function `Integer f(const Integer& a) { Integer r = a;  r += a;
return r; }'

   This function, when called (as in ` a = f(a); ') demonstrates a
similar kind of wasted copy. The returned value r must be copied out of
the function before it can be used by the caller. In GNU C++, there is
an alternative via the use of named return values.  Named return values
allow you to manipulate the returned object directly, rather than
requiring you to create a local inside a function and then copy it out
as the returned value. In this example, this can be done via `Integer
f(const Integer& a) return r(a) { r += a; return; }'

   A final guideline: The overloaded operators are very convenient, and
much clearer to use than procedural code. It is almost always a good
idea to make it right, *then* make it fast, by translating expression
code into procedural code after it is known to be correct.