Solution
Remember the introduction to the question? It
was: Forwarding functions are useful tools for handing off work to
another function or object, especially when the hand-off is done
efficiently.
This introduction gets to the heart of the
matter—efficiency.
There are two main enhancements that would make
this function more efficient. The first should always be done; the
second is a matter of judgment.
-
Pass the parameter by const& instead of
by value.
"Isn't that blindingly obvious?" you might ask.
No, it isn't, not in this particular case. Until as recently as
1997, the draft C++ language standard said that because a compiler
can prove that the parameter x will never be used for any
other purpose than passing it in turn to g(), the compiler
may decide to elide x completely (that is, to eliminate
x as unnecessary). For example, if the client code looks
something like this:
X my_x;
f( my_x );
then the compiler used to be allowed to
either:
-
Create a copy of my_x for
f()'s use (this is the parameter named x in
f()'s scope) and pass that to g()
-
Pass my_x directly to g()
without creating a copy at all, because it notices that the extra
copy will never be used except as a parameter to g()
The latter is nicely efficient, isn't it? That's
what optimizing compilers are for, aren't they?
Yes and yes, until the London meeting in July
1997. At that meeting, the draft was amended to place more
restrictions on the situations in which the compiler is allowed to
elide "extra" copies like this. This change was necessary to avoid
problems that can come up when compilers are permitted to wantonly
elide copy construction, especially when copy construction has side
effects. There are reasonable cases in which reasonable code may
legitimately rely on the number of copies actually made of an
object.
Today, the only situation in which a compiler
may still elide copy constructors is for the return value
optimization (see your favorite textbook for details) and for
temporary objects. This means that for forwarding functions like
f(), a compiler is now required to perform two copies.
Because we (as the authors of f()) know in this case that
the extra copy isn't necessary, we should fall back on our general
rule and declare x as a const X&
parameter.
Guideline
|
Prefer passing objects
by reference instead of by value, using const wherever
possible.
|
Note: If we'd been following this general rule
all along instead of trying to take advantage of detailed knowledge
about what the compiler is allowed to do, the change in the rules
wouldn't have affected us. This is a stellar example of why simpler
is better—avoid the dusty corners of the language as much as you
can, and strive never to rely on cute subtleties.
Guideline
|
Avoid the "dusty
corners" of a language; use the simplest techniques that are
effective.
|
-
Inline the function. This one is a matter of
judgment. In short, prefer to write all functions out-of-line by
default, and then selectively inline individual functions as
necessary only after you know that the performance gain from
inlining is actually needed.
Guideline
|
Avoid inlining or
detailed tuning until performance profiles prove the need.
|
If you inline the function, the positive side is
that you avoid the overhead of the extra function call to
f().
The negative side is that inlining f()
exposes f()'s implementation and makes client code depend
on it so that if f() changes, all client code must
recompile. Worse, client code now also needs at least the prototype
for function g(), which is a bit of a shame because client
code never actually calls g() directly and probably never
needed g()'s prototype before (at least, not as far as we
can tell from our example). And if g() itself were changed
to take other parameters of still other types, client code would
now depend on those classes' declarations, too.
Both inlining and not inlining can be valid
choices. It's a judgment call in which the benefits and drawbacks
depend on what you know about how (and how widely) f() is
used today, and how (and how often) it's likely to change in the
future.
|