You may not have thought much about the conditional operator
before now, but you have to admit, there is a lot there to
love. The standard devotes a whole page and a half of dense
standardese to this curious little beast, and we have just
scratched the surface. But I think you'll forgive me if I
take my language lawyer hat off now. I encourage you to
download the code for BOOST_FOREACH. There you
will find a version that works with other types of sequences
besides STL containers. BOOST_FOREACH is
currently under consideration for inclusion in Boost. You
can find the code in the
Boost Sandbox File Vault in the file foreach.zip.
Acknowledgements
Thanks go out to Andrei Alexandrescu, Chuck Allison, and
especially Scott Meyers for their detailed and thoughtful
reviews of this article. I am also deeply indebted to
Thorsten Ottosen for his Boost.Range library [8], which made it trivial to
extend BOOST_FOREACH to work with other
collection types besides STL containers. Finally, I would
like to thank Anson Tsao for his initial insight which led
to BOOST_FOREACH in the first place.
If you look at Listing 1, you'll notice that the
variables are declared in "if" statements. Text substitution
can be a dangerous thing. The if/else statements serve to
make FOREACH expand to one big statement, which
makes it play nicely with any surrounding code. I leave the
if/else statements out here because it would only obscure
the point.
The terms originally come from the fact that lvalues
can appear on the left side of an assignment, and rvalues
can only appear on the right.
It's not difficult to get the answer right in most
cases, but getting it right in all cases is a challenge. A
common trick uses the rules for binding to references.
Ordinarily, rvalues will not bind to references, and this
can be detected. Unfortunately, const-qualified rvalues will
bind to references, so the technique is not perfect.
In fact, on many compilers it doesn't work, sadly. It
works on Comeau and on gcc 3.3.3, but not on Visual C++ 7.1,
from my experiments.
The reason is because operator L & () const is
const- qualified and operator R () is not. Since the
expression rvalue_probe() is not const, in order to call a
const- qualified member function, the compiler must add a const
qualification to the rvalue_probe object. This is considered
a conversion. As a result, the conversion sequence using operator
R() is shorter than the sequence using operator L &()
const, and hence it is preferred.
Eric Niebler is an independent C++ consultant currently
working with Dave Abrahams and Boost Consulting. Formerly of
Microsoft Research, Eric has also written template libraries for Visual
C++. He is the author of the GRETA Regular Expression Template Library.
When not writing C++ for a living, he can often be found in coffee shops
around Seattle writing C++ for fun.