For your convenience Apress has placed some of the front
matter material after the index. Please use the Bookmarks
and Contents at a Glance links to access them.
Contents at a Glance
About the Author ....................................................................................................xix
About the Technical Reviewer ................................................................................xxi
Acknowledgments ................................................................................................xxiii
Preface ..................................................................................................................xxv
■
#include
................................................................... 1
■
Chapter 1: Templates
............................................................................................. 3
■
Chapter 2: Small Object Toolkit
........................................................................... 93
■
#include
................................................................... 119
■
Chapter 3: Static Programming
......................................................................... 121
■
Chapter 4: Overload Resolution
......................................................................... 173
■
Chapter 5: Interfaces
......................................................................................... 229
■
Chapter 6: Algorithms
....................................................................................... 275
■
Chapter 7: Code Generators
............................................................................... 327
■
Chapter 8: Functors
........................................................................................... 373
■
Chapter 9: The Opaque Type Principle
............................................................... 415
■
#include
................................................................. 475
■
Chapter 10: Refactoring
.................................................................................... 477
■
Chapter 11: Debugging Templates
..................................................................... 501
■
Chapter 12: C++0x
............................................................................................ 515
vii
■ CONTENTS AT A GLANCE
■
Appendix A: Exercises
....................................................................................... 527
■
Appendix B: Bibliography
.................................................................................. 533
Index ..................................................................................................................... 535
viii
PART 1
#include
#include
#include
CHAPTER 1
Templates
“C++ supports a variety of styles.”
Bjarne Stroustrup, A Perspective on ISO C++
Programming is the process of teaching something to a computer by talking to the machine in one of its
common languages. The closer to the machine idiom you go, the less natural the words become.
Each language carries its own expressive power. For any given concept, there is a language where its
description is simpler, more concise, and more detailed. In assembler, we have to give an extremely rich
and precise description for any (possibly simple) algorithm, and this makes it very hard to read back. On
the other hand, the beauty of C++ is that, while being close enough to the machine language, the language
carries enough instruments to enrich itself.
C++ allows programmers to express the same concept with different styles and good C++ looks more natural.
First you are going to see the connection between the templates and the style, and then you will dig into
the details of the C++ template system.
Given this C++ fragment:
double x = sq(3.14);
Can you guess what sq is? It could be a macro:
#define sq(x) ((x)*(x))
A function:
double sq(double x)
{
return x*x;
}
A function template:
template
inline scalar_t sq(const scalar_t& x)
{
return x*x;
}
3
CHAPTER 1 ■ TEMPLATES
A type (an unnamed instance of a class that decays to a double):
class sq
{
double s_;
public:
sq(double x)
: s_(x*x)
{}
operator double() const
{ return s_; }
};
A global object:
class sq_t
{
public:
typedef double value_type;
value_type operator()(double x) const
{
return x*x;
}
};
const sq_t sq = sq_t();
Regardless of how sq(3.14) is implemented, most humans can guess what sq(3.14) does just looking
at it. However, visual equivalence does not imply interchangeableness. If sq is a class, for example, passing a
square to a function template will trigger an unexpected argument deduction:
template void f(T x);
f(cos(3.14)); // instantiates f
f(sq(3.14)); // instantiates f. counterintuitive?
Furthermore, you would expect every possible numeric type to be squared as efficiently as possible, but
different implementations may perform differently in different situations:
std::vector v;
std::transform(v.begin(), v.end(), v.begin(), sq);
If you need to transform a sequence, most compilers will get a performance boost from the last
implementation of sq (and an error if sq is a macro).
4
CHAPTER 1 ■ TEMPLATES
The purpose of TMP is to write code that is:
•
•
•
Visually clear to human users so that nobody needs to look underneath.
Efficient in most/all situations from the point of view of the compiler.
1
Self-adapting to the rest of the program.
Self-adapting means “portable” (independent of any particular compiler) and “not imposing
constraints”. An implementation of sq that requires its argument to derive from some abstract base class
would not qualify as self-adapting.
The true power of C++ templates is style. Compare the following equivalent lines:
double x1 = (-b + sqrt(b*b-4*a*c))/(2*a);
double x2 = (-b + sqrt(sq(b)-4*a*c))/(2*a);
All template argument computations and deductions are performed at compile time, so they impose
no runtime overhead. If the function sq is properly written, line 2 is at least as efficient as line 1 and easier to
read at the same time.
Using sq is elegant:
•
•
•
It makes code readable or self-evident
It carries no speed penalty
It leaves the program open to future optimizations
In fact, after the concept of squaring has been isolated from plain multiplication, you can easily plug in
specializations:
template
inline scalar_t sq(const scalar_t& x)
{
return x*x;
}
template <>
inline double sq(const double& x)
{
// here, use any special algorithm you have!
}
1Looselyspeaking,that’sthereasonforthe“meta”prefixin“metaprogramming”.
5