TXTBK - C++ - Intermediate Gems - 4.7
TXTBK - C++ - Intermediate Gems - 4.7
“Steve taught me C++. This was back in 1982 or 1983, I think—he had just returned
from an internship sitting with Bjarne Stroustrup [inventor of C++] at Bell Labs.
Steve is one of the unsung heroes of the early days, and anything Steve writes is
on my A-list of things to read. This book is an easy read and collects a great deal
of Steve’s extensive knowledge and experience. It is highly recommended.“
—Stan Lippman, coauthor of C++ Primer, Fourth Edition
“I agree with [the author’s] assessment of the types of programmers. I have encoun-
tered the same types in my experience as a developer and a book like this will go
far to help bridge their knowledge gap.... I think this book complements other
books, like Effective C++ by Scott Meyers. It presents everything in a concise and
easy-to-read style.“
—Moataz Kamel, senior software designer, Motorola Canada
“Dewhurst has written yet another very good book. This book should be required
reading for people who are using C++ (and think that they already know every-
thing in C++).“
—Clovis Tondo, coauthor of C++ Primer Answer Book
This page intentionally left blank
C++ Common Knowledge
This page intentionally left blank
C++ Common
Knowledge
Essential Intermediate Programming
Stephen C. Dewhurst
All rights reserved. Printed in the United States of America. This publication is protected by copy-
right, and permission must be obtained from the publisher prior to any prohibited reproduction,
storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical,
photocopying, recording, or likewise. For information regarding permissions, write to:
ISBN 0-321-32192-8
Text printed in the United States at Demand Print Center in Old Tappan, New Jersey.
5th Printing July 2009
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
A Note on Typographical Conventions . . . . . . . . . . . . . . . . . . . . . xix
vii
viii ❘ Contents
Item 21 Overloading and Overriding Are Different . . . . . . . . . . 75
Item 22 Template Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Item 23 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Item 24 Member Function Lookup . . . . . . . . . . . . . . . . . . . . . . . 87
Item 25 Argument Dependent Lookup . . . . . . . . . . . . . . . . . . . . 89
Item 26 Operator Function Lookup . . . . . . . . . . . . . . . . . . . . . . . 91
Item 27 Capability Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Item 28 Meaning of Pointer Comparison . . . . . . . . . . . . . . . . . . 97
Item 29 Virtual Constructors and Prototype . . . . . . . . . . . . . . . . 99
Item 30 Factory Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Item 31 Covariant Return Types . . . . . . . . . . . . . . . . . . . . . . . . . 107
Item 32 Preventing Copying . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Item 33 Manufacturing Abstract Bases . . . . . . . . . . . . . . . . . . . 113
Item 34 Restricting Heap Allocation . . . . . . . . . . . . . . . . . . . . . 117
Item 35 Placement New . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
Item 36 Class-Specific Memory Management . . . . . . . . . . . . . . 123
Item 37 Array Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Item 38 Exception Safety Axioms . . . . . . . . . . . . . . . . . . . . . . . 131
Item 39 Exception Safe Functions . . . . . . . . . . . . . . . . . . . . . . . 135
Item 40 RAII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Item 41 New, Constructors, and Exceptions . . . . . . . . . . . . . . . 143
Item 42 Smart Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Item 43 auto_ptr Is Unusual . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Item 44 Pointer Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Item 45 Template Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 153
Item 46 Class Template Explicit Specialization . . . . . . . . . . . . . 155
Item 47 Template Partial Specialization . . . . . . . . . . . . . . . . . . 161
Item 48 Class Template Member Specialization . . . . . . . . . . . . 165
Item 49 Disambiguating with Typename . . . . . . . . . . . . . . . . . 169
Item 50 Member Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Contents ❘ ix
Item 51 Disambiguating with Template . . . . . . . . . . . . . . . . . . 179
Item 52 Specializing for Type Information . . . . . . . . . . . . . . . . 183
Item 53 Embedded Type Information . . . . . . . . . . . . . . . . . . . . 189
Item 54 Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Item 55 Template Template Parameters . . . . . . . . . . . . . . . . . . 199
Item 56 Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Item 57 Template Argument Deduction . . . . . . . . . . . . . . . . . . 209
Item 58 Overloading Function Templates . . . . . . . . . . . . . . . . . 213
Item 59 SFINAE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Item 60 Generic Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Item 61 You Instantiate What You Use . . . . . . . . . . . . . . . . . . . 225
Item 62 Include Guards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Item 63 Optional Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Index of Code Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
This page intentionally left blank
Preface
A successful book is not made of what is in it, but what is left out of it.
—Mark Twain
…a writer who questions the capacity of the person at the other end of the
line is not a writer at all, merely a schemer.
—E.B. White
When he took over the editorship of the late C++ Report, the quick Herb
Sutter asked me to write a column on a topic of my choosing. I agreed,
and I chose to call the column “Common Knowledge.” It was supposed to
be, in Herb’s words, “a regular summary of basic lore that every working
C++ programmer should know—but can’t always.” After a couple of
columns in that vein, however, I became interested in template metapro-
gramming techniques, and the topics treated in “Common Knowledge”
from that point on were far from common.
However, the problem in the C++ programming industry that motivated
my original choice of column remains. I commonly encounter the follow-
ing types of individuals in my training and consulting work:
■ Domain experts who are expert C programmers but who have only
basic knowledge of (and perhaps some animosity toward) C++
■ Talented new hires direct from university who have an academic
appreciation for the C++ language but little production C++
experience
xi
xii ❘ Preface
■ Expert Java programmers who have little C++ experience and who
have a tendency to program in C++ the way one would program
in Java
■ C++ programmers with several years of experience maintaining
existing C++ applications but who have not been challenged to learn
anything beyond the basics required for maintenance
I want to be immediately productive, but many of the people with whom
I’m working or who I’m training require preliminary education in vari-
ous C++ language features, patterns, and coding techniques before we
can get down to business. Worse, I suspect that most C++ code is written
in ignorance of at least some of these basics and is therefore not what
most C++ experts would consider to be production quality.
This book addresses this pervasive problem by providing essential, com-
mon knowledge that every professional C++ programmer needs to know,
in a form that is pared to its essentials and that can be efficiently and
accurately absorbed. Much of the information is already available from
other sources or is part of that compendium of unwritten information
that all expert C++ programmers know. The advantage is that this mate-
rial resides in one place and was selected according to what my training
and consulting experience over many years has shown are the most com-
monly misunderstood and most useful language features, concepts, and
techniques.
Perhaps the most important aspect of the sixty-three short items that
make up this book is what they leave out, rather than what they contain.
Many of these topics have the potential to become complex. An author’s
ignorance of these complexities could result in an uninformed descrip-
tion that could mislead the reader, but an expert discussion of a topic in
its full complexity could inundate the reader. The approach used here is
to filter out needless complexity in the discussion of each topic. What
remains, I hope, is a clear distillation of the essentials required for pro-
duction C++ programming. C++ language wonks will recognize, there-
fore, that I’ve left out discussion of some issues that are interesting and
even important from a theoretical perspective, but the ignorance of
which does not commonly affect one’s ability to read and write produc-
tion C++ code.
Another motivation for this book came as I was engaged in conversation
with a group of well-known C++ experts at a conference. There was a
Preface ❘ xiii
general pall or depression among these experts that modern C++ is so
complex that the “average” programmer can no longer understand it.
(The specific issue was name binding in the context of templates and
namespaces. Yes, getting worked up about such a topic does imply the
need for more play with normal children.) On reflection, I’d have to say
our attitude was pretentious and our gloom unwarranted. We “experts”
have no such problems, and it’s as easy to program in C++ as it is to speak
a (vastly more complex) natural language, even if you can’t diagram the
deep structure of your every utterance. A recurring theme of this book is
that while the full description of the minutia of a particular language fea-
ture may be daunting, day-to-day use of the feature is straightforward
and natural.
Consider function overloading. A full description occupies a large chunk
of the standard and whole or multiple chapters in many C++ texts. And
yet, when faced with
void f( int );
void f( const char * );
//…
f( "Hello" );
.
—Stephen C. Dewhurst
Carver, Massachusetts
January 2005
Acknowledgments
xvii
xviii ❘ Acknowledgments
shape the set of topics treated in this book. Andrei Alexandrescu’s work
inspired me to experiment with template metaprogramming rather than
do what I was supposed to be doing, and both Herb Sutter’s and Jack
Reeves’s work with exceptions has helped me to understand better how
exceptions should be employed.
I’d also like to thank my neighbors and good friends Dick and Judy Ward,
who periodically ordered me away from my computer to work the local
cranberry harvest. For one whose professional work deals primarily in
simplified abstractions of reality, it’s intellectually healthful to be shown
that the complexity involved in convincing a cranberry vine to bear fruit
is a match for anything a C++ programmer may attempt with template
partial specialization.
Sarah G. Hewins and David R. Dewhurst provided, as always, both valu-
able assistance and important impediments to this project.
I like to think of myself as a quiet person of steady habits, given more to
calm reflection than strident demand. However, like those who undergo
a personality transformation once they’re behind the wheel of an auto-
mobile, when I get behind a manuscript I become a different person
altogether. Addison-Wesley’s terrific team of behavior modification
professionals saw me through these personality issues. Chanda Leary-
Coutu worked with Peter Gordon and Kim Boedigheimer to translate
my rantings into rational business proposals and shepherd them
through the powers-that-be. Molly Sharp and Julie Nahil not only
turned an awkward word document into the graceful pages you see
before you, they managed to correct many flaws in the manuscript while
allowing me to retain my archaic sentence structure, unusual diction,
and idiosyncratic hyphenation. In spite of my constantly changing
requests, Richard Evans managed to stick to the schedule and produce
not one, but two separate indexes. Chuti Prasertsith designed a gorgeous,
cranberry-themed cover. Many thanks to all.
A Note on Typographical
Conventions
xix
This page intentionally left blank
❘
Item 1 Data Abstraction
1
2 ❘ Item 1 Data Abstraction
Deal Priceable
Option
AmOption EurOption
3
4 ❘ Item 2 Polymorphism
fact, it’s not uncommon, and is often desirable, for a base class to consist
of nothing but interface (see Capability Queries [27, 93]).
Of course, there’s a catch. For this leveraging to work, a properly designed
polymorphic class must be substitutable for each of its base classes. In
other words, if generic code written to the Option interface gets an
AmOption object, that object had better behave like an Option!
code
Option
written to
Option
price()
interface
update()
AmOption EurOption
price() price()
❘
Figure 2 A polymorphic contractor and its subcontractors. The Option base class
specifies a contract.
Item 2 Polymorphism ❘5
On the other hand, if I were to call the price function of two different
interfaces to the same object, I’d better get the same result. Essentially,
either call should bind to the same function:
AmOption *d = new AmOption;
Option *b = d;
d->price(); // if this calls AmOption::price...
b->price(); // ...so should this!
The contract provided by the base class is what allows the “polymorphic”
code written to the base class interface to work with specific options while
promoting healthful ignorance of their existence. In other words, the
polymorphic code may be manipulating AmOption and EurOption
objects, but as far as it’s concerned they’re all just Options. Various concrete
Option types can be added and removed without affecting the generic
code that is aware only of the Option base class. If an AsianOption
shows up at some point, the polymorphic code that knows only about
Options will be able to manipulate it in blissful ignorance of its specific
type, and if it should later disappear, it won’t be missed.
By the same token, concrete option types such as AmOption and EurOption
need to be aware only of the base classes whose contracts they implement
and are independent of changes to the generic code. In principle, the base
class can be ignorant of everything but itself. From a practical perspec-
tive, the design of its interface will take into account the requirements of
its anticipated users, and it should be designed in such a way that derived
classes can easily deduce and implement its contract (see Template
Method [22, 77]). However, a base class should have no specific knowledge
of any of the classes derived from it, because such knowledge inevitably
makes it difficult to add or remove derived classes in the hierarchy.
In object-oriented design, as in life, ignorance is bliss (see also Virtual
Constructors and Prototype [29, 99] and Factory Method [30, 103]).
This page intentionally left blank
❘
Item 3 Design Patterns
Anyone who is not already familiar with design patterns may, after a brief
survey of the field, come away with the impression that design patterns
are a lot of marketing hype, are just some simple coding techniques, or are
the playthings of computer scientists who really should get out more.
While each of these impressions carries a grain of truth, design patterns
are an essential component of the professional C++ programmer’s
toolkit.
A “design pattern” is a recurring architectural theme that provides a
solution to a common design problem within a particular context and
describes the consequences of this solution. A design pattern is more than
a simple description of a technique; it’s a named capsule of design wis-
dom gleaned from successful existing practice, written in such a way that
it can be easily communicated and reused. Patterns are about program-
mer to programmer communication.
From a practical perspective, design patterns have two important proper-
ties. First, they describe proven, successful design techniques that can be
customized in a context-dependent way to new design situations. Second,
and perhaps more important, mentioning the application of a particular
pattern serves to document not only the technique that is applied but also
the reasons for its application and the effect of having applied it.
This sort of thing is nothing new. Consider an analogy from the field of
algorithms. (Algorithms are not design patterns, and they’re not “code
patterns.” They’re algorithms, and this is an analogy.) Consider the fol-
lowing statement that I might make to a colleague: “I have an unsorted
sequence that I have to search a number of times. Therefore, I’m going to
quick sort it and use binary search to perform each lookup.” The ability to
use the terms “quick sort” and “binary search” is of inestimable value not
only in design but also in communicating that design to an educated col-
league. When I say “quick sort,” my colleague knows that the sequence I’m
sorting is in a random access structure, that it will probably be sorted
within O(nlg2n) time, and that the elements in the sequence may be
compared with a less-than-like operator. When I say “binary search,” my
7
8 ❘ Item 3 Design Patterns
colleague knows (even if I hadn’t earlier mentioned “quick sort”) that the
sequence is sorted, that I will locate the item of interest within O(lg2n)
comparisons, and that an appropriate operation is available to compare
sequence elements. Shared knowledge of, and a standard vocabulary for,
standard algorithms permits not only efficient documentation but also
efficient criticism. For example, if I planned to perform this search and
sort procedure on a singly linked list structure, my colleague would
immediately smirk and point out that I couldn’t use quick sort and prob-
ably wouldn’t want to use binary search.
Until the advent of design patterns, we missed these advantages in docu-
mentation, communication, and efficient smirking with our object-
oriented designs. We were forced into low-level descriptions of our
designs, with all the inefficiency and imprecision that entails. It’s not that
techniques for sophisticated object-oriented design didn’t exist; it’s that
the techniques were not readily available to the entire programming com-
munity under a shared, common terminology. Design patterns address
that problem, and we can now describe object-oriented designs as effi-
ciently and unambiguously as algorithmic designs.
For example, when we see that the Bridge pattern has been applied to a
design, we know that at a simple mechanical level an abstract data type
implementation has been separated into an interface class and an imple-
mentation class. Additionally, we know that the reason this was done was
to separate strongly the interface from the implementation so that
changes to the implementation would not affect users of the interface. We
also know a runtime cost exists for this separation, how the source code
for the abstract data type should be arranged, and many other details. A
pattern name is an efficient, unambiguous handle to a wealth of informa-
tion and experience about a technique, and careful, accurate use of pat-
terns and pattern terminology in design and documentation clarifies
code and designs.
Patterns wonks sometimes describe design patterns as a form of literature
(they really do) that follows a certain formal structure. Several common
variants are in use, but all forms contain four essential parts.
First, a design pattern must have an unambiguous name. For example, the
term “wrapper” is useless for a design pattern, because it is already in
common use and has dozens of meanings. Using a term like “Wrapper” as
a pattern name would lead only to confusion and misunderstanding.
Item 3 Design Patterns ❘9
Instead, the different design techniques that formerly went under the
name “wrapper” are now designated by the pattern names “Bridge,”
“Strategy,”“Façade,”“Object Adapter,” and probably several others. Use of
a precise pattern name has a clear advantage over using a less precise
term, in the same way that “binary search” is a more precise and useful
term than “lookup.”
Second, the pattern description must define the problem addressed by the
pattern. This description may be relatively broad or narrow.
Third, the pattern description describes the problem’s solution. Depend-
ing on the statement of the problem, the solution may be rather high level
or relatively low level, but it should still be general enough to customize
according to the various contexts in which the problem may occur.
Fourth, the pattern description describes the consequences of applying
the pattern to the context. How has the context changed for better or
worse after application of the pattern?
Will knowledge of patterns make a bad designer a good designer? Time
for another analogy: Consider one of those painful mathematics courses
you may have been forced to undergo, in which the final examination is to
prove a number of theorems in a certain area of mathematics. How do
you get out of such a course alive? One obvious way is to be a genius.
Starting from first principles, you develop the underpinnings of an entire
branch of mathematics and eventually prove the theorems. A more prac-
tical approach would be to memorize and internalize a large number of
theorems in that area of mathematics and use whatever native mathemat-
ical ability, inspiration, or good luck you have at your disposal to select
the appropriate subsidiary theorems and combine them with some logical
glue to prove the new theorems. This approach is advantageous even for
our fictitious genius, because a proof built upon established theorems is
more efficient to construct and easier to communicate to mere mortals.
Familiarity with subsidiary theorems does not, of course, guarantee that a
poor mathematician will be able to pass the test, but such knowledge will at
least enable that person to understand the proof once it has been produced.
In a similar vein, developing a complex object-oriented design from first
principles is probably going to be tedious, and communication of the even-
tual design difficult. Composition of design patterns to produce an object-
oriented design is similar to use of subsidiary theorems in mathematics to
10 ❘ Item 3 Design Patterns
11
12 ❘ Item 4 The Standard Template Library
References are often confused with pointers, perhaps because C++ com-
pilers often implement references as pointers, but they are not pointers
and do not behave like pointers.
Three major differences between references and pointers are that there
are no null references, all references require initialization, and a reference
always refers to the object with which it is initialized. In the previous
example, the reference ra will refer to a for its entire lifetime. Most erro-
neous uses of references stem from misunderstanding these differences.
Some compilers may catch an obvious attempt to create a null reference:
Employee &anEmployee = *static_cast<Employee*>(0); // error!
However, the compiler may not detect less obvious attempts to create a
null reference, which will cause undefined behavior at runtime:
Employee *getAnEmployee();
//...
Employee &anEmployee = *getAnEmployee(); // probably bad code
if( &anEmployee == 0 ) // undefined behavior
13
14 ❘ Item 5 References Are Aliases, Not Pointers
In the call to swap above, a aliases x, and b aliases y, for the duration of
the call. Note that the object to which a reference refers needn’t have a
name, so a reference may be used to give a convenient name to an
unnamed object:
int grades[MAX];
//...
swap( grades[i], grades[j] );
After the formal arguments a and b of swap are initialized with the actual
arguments grades[i] and grades[j], respectively, those two nameless
array elements can be manipulated through the aliases a and b. This
property may be used more directly in order to simplify and optimize.
Item 5 References Are Aliases, Not Pointers ❘ 15
Consider the following function that sets a particular element of a two-
dimensional array:
inline void set_2d( float *a, int m, int i, int j ) {
a[i*m+j] = a[i*m+j] * a[i*m+i] + a[i*m+j]; // oops!
}
We can replace the line commented “oops!” with a simpler version that
employs a reference and that has the additional advantage of being correct.
(Did you catch the error? I didn’t the first time around.)
inline void set_2d( float *a, int m, int i, int j ) {
float &r = a[i*m+j];
r = r * r + r;
}
Array formal arguments are problematic. The major surprise in store for
the C/C++ novice is that there are no array formal arguments, because an
array is passed as a pointer to its first element.
void average( int ary[12] ); // formal arg is int *
//...
int anArray[] = { 1, 2, 3 }; // three-element array
const int anArraySize =
sizeof(anArray)/sizeof(anArray[0]); // == 3
average( anArray ); // legal!
If, on the other hand, the precise value of the array bound is important,
and you want the function to accept only arrays with a particular number
of elements, you may consider a reference formal argument:
void average( int (&ary)[12] );
17
18 ❘ Item 6 Array Formal Arguments
Now our function will accept only integer arrays of size 12.
average( anArray ); // error! anArray is int [3]!
but a more traditional solution is to pass the size of the array explicitly.
void average_n( int ary[], int size );
It should be clear from this discussion that one of the major problems
with using arrays as function arguments is that the size of the array must
be encoded explicitly in the type of the formal argument, passed as a sep-
arate argument, or indicated by a “terminator” value within the array
itself (such as the '\0' that is used to indicate the end of a character
array used as a string). Another difficulty is that—no matter how it is
declared—an array is often manipulated through a pointer to its first ele-
ment. If that pointer is passed as the actual argument to a function, our
previous trick of declaring a reference formal argument will not help us.
int *anArray2 = new int[anArraySize];
//...
average( anArray2 ); // error! can't init int(&)[n] with int *
average_n( anArray, anArraySize ); // OK...
Note that the second (and subsequent) bounds are not decayed, because
otherwise it would not be possible to perform pointer arithmetic with the
formal argument (see Pointer Arithmetic [44, 149]). As noted previously,
it’s often a good idea to let your reader know you expect the actual argu-
ment to be an array:
void process( int ary[][20] ); // still a pointer, but clearer
Simply put, array formal arguments are a pain in the neck. Approach
with caution.
This page intentionally left blank
❘
Item 7 Const Pointers and
Pointers to Const
Before you start tossing const qualifiers into your pointer declarations,
you first have to decide what it is that you want to be const: the pointer,
the object to which you’re pointing, or both. In the declaration of pct, the
pointer is not const, but the object it points to is considered to be const;
that is, the const qualifier modifies the T base type, not the * pointer
modifier. In the case of cpt, we’re declaring a constant pointer to a non-
constant object; the const qualifier modifies the * pointer modifier, not
the T base type.
To further confuse the syntactic issues surrounding pointers and const,
the order of the declaration specifiers (that is, everything in the pointer
declaration that occurs before the first * modifier) is immaterial. For
instance, the following two declarations declare variables of exactly the
same type:
const T *p1; // ptr to const T
T const *p2; // also ptr to const T
The first form is more traditional, but many C++ experts now recom-
mend the second form. The reasoning is that the second form is less
prone to misunderstanding because the declaration can be read back-
ward, as in “pointer to constant T.” It doesn’t really matter which form we
21
22 ❘ Item 7 Const Pointers and Pointers to Const
T *pt T at
const *pct
The C++ standard tells us that such an assignment will have undefined
results; we don’t know precisely what will happen, but whatever it is won’t
be good. Of course, we can use a cast to perform the conversion explicitly.
pt = const_cast<T *>(pct); // not an error, but inadvisable
*pt = aT; // attempt to change const object!
T *pt
It’s legal to declare a pointer to a pointer. This is what the C++ standard
calls a “multilevel” pointer.
int *pi; // a ptr
int **ppi; // a two-level multilevel ptr
int ***pppi; // a three-level multilevel ptr
Although it’s rare to encounter multilevel pointers with more than two
levels, we do see pointers to pointers in two common situations. The first
is when we declare an array of pointers.
Shape *picture[MAX]; // array of ptr to Shape
Because an array name decays into a pointer to its first element (see Array
Formal Arguments [6, 17]), the name of an array of pointers is also a
pointer to a pointer.
Shape **pic1 = picture;
We most often see this usage in the implementation of a class that man-
ages a buffer of pointers:
template <typename T>
class PtrVector {
public:
explicit PtrVector( size_t capacity )
: buf_(new T *[capacity]), cap_(capacity), size_(0) {}
//...
private:
T **buf_; // ptr to array of ptr to T
size_t cap_; // capacity
size_t size_; // size
};
//...
PtrVector<Shape> pic2( MAX );
25
26 ❘ Item 8 Pointers to Pointers
The same confusion often occurs when const is involved. We know that
it’s legal to convert a pointer to non-const to a pointer to const (see Const
Pointers and Pointers to Const [7, 21]), but we may not convert a pointer to
pointer to non-const to a pointer to pointer to const:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char **ps = a; // error!
This page intentionally left blank
❘
Item 9 New Cast Operators
C++ introduced another way of saying the same thing with the function-
style cast syntax:
typedef char *PChar;
hopeItWorks =
PChar( 0x00ff0000 ); // function-style/old-style cast
A function-style cast may look more civilized than its dread forebear, but
it’s just as nasty; avoid both of them like the plague.
Honest programmers use the new cast operators because they allow you
to say more precisely what you mean, and mean more accurately what
you say. There are four of them, and each has a specific purpose.
The const_castoperator allows you to add or remove const and
volatile type qualifiers in an expression’s type:
In this code, we’ve used const_cast to strip a const type qualifier from
the return type of getEmployee. We could have used an old-style cast to
achieve the same result,
anEmployee = (Person *)getEmployee();
29
30 ❘ Item 9 New Cast Operators
but the const_cast is superior for a couple of reasons. First, it’s obvious
and hideous. It sticks out of the code like a sore thumb, and that’s a good
thing, because casts in any form are dangerous. They should be painful to
write, because you should use them only if you have to use them. They
should be easy to find, because casts are the “usual suspects” one exam-
ines first whenever a bug appears. Second, a const_cast is less powerful
than an old-style cast because it will affect only type qualifiers. This
restriction is a good thing as well, because it allows us to say more pre-
cisely what our intent is. Using an old-style cast tells the compiler to shut
up because you intend that the return type of getEmployee be converted
to Person *. Use of a const_cast tells the compiler to shut up because
you intend to strip a const from the return type of getEmployee. There
is not a big difference in these two statements (although they’re both pretty
disrespectful, really) unless some maintenance occurs to the getEmployee
function:
const Employee *getEmployee(); // big change!
The gag rule enforced by the old-style cast is still in effect; the improper
conversion of const Employee * to Person * will not be flagged by the
compiler, but the compiler will complain about the const_cast, because
that drastic a conversion is beyond its capabilities. In short, the
const_cast is preferred to the old-style cast because it’s more hideous,
harder to use, and less powerful.
The static_cast operator is used for casts that are relatively portable
across platforms. Most commonly, it is used to cast “down” an inheritance
hierarchy from a base class pointer or reference toward a derived class
pointer or reference (see also Capability Queries [27, 93]):
Shape *sp = new Circle;
Circle *cp = static_cast<Circle *>(sp); // downcast
In this case, the static_cast will result in correct code, because sp actu-
ally does refer to a Circle object. However, if sp had pointed instead to
some other type of Shape, we would have likely gotten a runtime error of
some sort when we used cp. To reiterate, these new cast operators are
safer than old-style casts, but they’re not necessarily safe.
Note that a static_cast may not change type qualifiers the way a
const_cast can. This implies that it is sometimes necessary to use
Item 9 New Cast Operators ❘ 31
a sequence of two new cast operators to achieve what a single old-style
cast could do:
const Shape *getNextShape() { ... }
//...
Circle *cp =
static_cast<Circle *>(const_cast<Shape *>(getNextShape()));
33
34 ❘ Item 10 Meaning of a Const Member Function
}
return computedValue_;
}
private:
static int expensiveOperation();
int *buffer_;
bool isComputed_;
int computedValue_;
};
Overload resolution will match the address of a const object with a this
pointer that points to a const. As another example, consider the following
non-member binary operator with two const arguments:
X operator +( const X &, const X & );
Like many areas of social life, proper programming with const in C++ is
technically simple but morally challenging.
❘
Item 11 The Compiler Puts
Stuff in Classes
37
38 ❘ Item 11 The Compiler Puts Stuff in Classes
There is. If it’s important to have a class type that is guaranteed to be like a
C struct, you can define a POD (standardese for “plain old data”). Certainly,
built-in types such as int, double, and the like are PODs, but a C struct
or union-like declaration is also a POD.
struct S { // a POD-struct
int a;
double b;
};
If you’re not willing to deal only with PODs, what does all this meddling
by the compiler imply about how you should manipulate class objects? It
implies that you should manipulate class objects at a high level rather
than as collections of bits. A higher-level operation will do “the same
thing” from platform to platform, but it may accomplish it differently.
For example, if you want to copy a class object, never use a block copy
such as the standard memcpy function or the hand-coded equivalent,
because that’s for copying storage, not objects (Placement New [35, 119]
discusses this difference). Rather, you should use the object’s own initial-
ization or assignment operations. An object’s constructor is where the
compiler sets up the hidden mechanism that implements the object’s vir-
tual functions, and the like. Simply blasting a bunch of bits into uninitial-
ized storage may not do the right thing. Similarly, a copy of one object to
another must take care not to overwrite these internal class mechanisms.
For example, assignment never changes the value of an object’s virtual
function table pointers; they’re set by the constructor and never changed
for the life of the object. A bit blast may well destroy that delicate internal
structure. (See also Copy Operations [13, 45].)
Item 11 The Compiler Puts Stuff in Classes ❘ 39
Another common problem is the assumption that a particular member of
a class is resident at a given offset within an object. In particular, it’s not
uncommon for overly clever code to assume either that the virtual function
table pointer is at offset zero (that is, it’s the first thing in the class) or that
the first declared data member is at offset zero. Neither of these assump-
tions is correct more than about half the time, and both are (of course)
never correct simultaneously.
struct T { // not a POD
int a_; // offset of a_ unknown
virtual void f(); // offset of vptr unknown
};
I’m not going to continue in this vein, because such a list of no-no’s
would be long, tedious, and possibly tasteless. But the next time you find
yourself making low-level assumptions about the internal structure of
your classes, pause, reflect, and get your mind out of the gutter!
This page intentionally left blank
❘
Item 12 Assignment and Initialization
Are Different
41
42 ❘ Item 12 Assignment and Initialization Are Different
char *s_;
};
Copying is such a pervasive operation that it’s even more important than
usual to follow convention. These operations are always declared as a pair,
with the signatures above (but see auto_ptr Is Unusual [43, 147] and Pre-
venting Copying [32, 111]). That is, for a class X, the copy constructor
should be declared X(const X &), and the copy assignment operator
should be declared X &operator =(const X &). It’s common and often a
good idea to define a member swap function if a member implemen-
tation of swap has a performance or exception safety advantage over a
traditional non-member swap. An implementation of a typical non-
member swap is straightforward:
template <typename T>
void swap( T &a, T &b ) {
T temp(a); // T's copy ctor
a = b; // T's copy assignment
b = temp; // T's copy assignment
}
45
46 ❘ Item 13 Copy Operations
Remember the old comedy routine in which we’re told how to get a mil-
lion dollars and never pay any taxes on it? First, you get a million dol-
lars.… In a similar vein, we can show how to write an exception safe copy
assignment operation. First, you get an exception safe copy constructor
and an exception safe swap operation. The rest is easy:
Handle &Handle::operator =( const Handle &that ) {
Handle temp( that ); // exception safe copy construction
swap( temp ); // exception safe swap
return *this; // we assume temp's destruction won't throw
}
This technique works particularly well for “handle” classes, that is, classes
that consist mostly or entirely of a pointer to their implementations. As
we saw in an earlier example in this item, writing exception safe swaps for
such classes is both trivial and efficient.
The subtle point of this implementation of copy assignment is that the
behavior of copy construction must be compatible with that of copy
assignment; they’re different operations, but there is a pervasive,
idiomatic assumption that they will produce indistinguishable results.
That is, whether one writes
Handle a = ...
Handle b;
b = a; // assign a to b
or
Handle a = ...
Handle b( a ); // initialize b with a
Note that no “generic” pointer exists that can point to any type of func-
tion the way a void * pointer can refer to any kind of data. Also note that
the address of a non-static member function is not a pointer, so we can’t
49
50 ❘ Item 14 Function Pointers
Once we’ve determined our course of action, a different part of our code
can focus on if and when to execute the action, without being concerned
with what the action is:
if( ftemp >= 451 ) { // if there's a fire...
if( fireAction ) // ...and an action to execute...
fireAction(); // ...execute it!
}
The type of the pointer is used to select among the various candidate
functions. In this case, fireAction has type void(*)(), so the first
jumpIn is selected.
It’s unfortunate that pointers to class members have the term “pointer” in
their descriptions, because they don’t contain addresses and don’t behave
like pointers.
The syntax for declaring a pointer to member is really not too horrible (if
you’re already resigned to the declarator syntax for regular pointers):
int *ip; // pointer to an int
int C::*pimC; // pointer to an int member of C
The name weird1 has the type pointer to const pointer to pointer to
pointer to void. The name weird2 has the type pointer to const pointer
to a member of B to a pointer to a member of A, which is a pointer to
void. (This is just an example, and you wouldn’t normally expect to see a
declaration this complex or this silly.)
A regular pointer contains an address. If you dereference a pointer, you
get the object at that address:
int a = 12;
ip = &a;
*ip = 0;
a = *ip;
53
54 ❘ Item 15 Pointers to Class Members Are Not Pointers
When we set the value of pimC to &C::a_, we’re effectively setting pimC
with the offset of a_ within C. Let’s be clear: Unless a_ is a static member,
using & in the expression &C::a_ does not give us an address; it gives us
an offset. Note that this offset applies to any object of type C; that is, if the
member a_ can be found 12 bytes from the start of one C object, it will be
found 12 bytes from the start of any other C object.
Given an offset of a member within a class, we need the address of an
object of that class in order to get to the data member at that offset. That’s
where the unusual-looking .* and ->* operators enter. When we write
pC->*pimC, we are requesting that the address in pC be augmented by the
offset in pimC in order to access the appropriate data member in the C
object referred to by pC. When we write aC.*pimC, we are requesting that
the address of aC be augmented by the offset in pimC in order to access the
appropriate data member in the C object referred to by pC.
Pointers to data members are not as commonly used as pointers to member
functions, but they are handy for illustrating the concept of contravariance.
Item 15 Pointers to Class Members Are Not Pointers ❘ 55
There is a predefined conversion from a pointer to a derived class to a
pointer to any of its public base classes. We often say that there is an
is-a relationship from the derived class to its public base classes, and this
relationship often arises naturally from an analysis of the problem
domain (see Polymorphism [2, 3]). Therefore, we can state (for example)
that a Circle is-a Shape through public inheritance, and C++ backs us
up by providing an implicit conversion from Circle * to Shape *. No
implicit conversion exists from a Shape * to a Circle * because such a
conversion would not make sense; many different types of Shape may
exist, and not all of them are Circles. (It also just sounds silly to say, “A
Shape is a Circle.”)
It makes sense to say that a Circle contains all the data members of its
Shape base class (that is, it inherits those members from Shape), and
C++ backs us up with an implicit conversion from a pointer to member
of a Shape to a pointer to member of a Circle. It doesn’t make sense to
say that a Shape contains all the data members of a Circle (Shape doesn’t
inherit anything from Circle), and C++ reminds us of that by disallowing
the conversion from pointer to member of Circle to pointer to member
of Shape.
❘
Item 16 Pointers to Member Functions
Are Not Pointers
When you take the address of a non-static member function, you don’t
get an address; you get a pointer to member function.
class Shape {
public:
//...
void moveTo( Point newLocation );
bool validate() const;
virtual bool draw() const = 0;
//...
};
class Circle : public Shape {
//...
bool draw() const;
//...
};
//...
void (Shape::*mf1)( Point ) = &Shape::moveTo; // not a pointer
57
58 ❘ Item 16 Pointers to Member Functions Are Not Pointers
The ->* and .* operators must be parenthesized because they have lower
precedence than the () operator, and we have to first find out what func-
tion to call before we call it! This is entirely analogous to the use of paren-
theses in an expression such as (a+b)*c, where we want to ensure that the
lower-precedence addition is carried out before the higher-precedence
multiplication.
Note that there is no such thing as a “virtual” pointer to member func-
tion. Virtualness is a property of the member function itself, not the
pointer that refers to it.
mf2 = &Shape::draw; // draw is virtual
(pShape->*mf2)(); // call Circle::draw
The main confusion with pointer to function and pointer to array decla-
rations arises because the function and array modifiers have higher prece-
dence than the pointer modifier, so parentheses are often required.
int *f1(); // function that returns int *
int (*fp1)(); // ptr to function that returns int
Note that both the argument and return types contribute to the type of a
function or function pointer.
char *(*fp4)(int,int);
char *(*fp5)(short,short) = 0;
fp4 = fp5; // error! type mismatch
61
62 ❘ Item 17 Dealing with Function and Array Declarators
that works just fine in Java but is not legal C++. The correct declaration of
an array of function pointers puts the name being declared in the same
location that it appears in a simple pointer to function. Then we say we
want an array of those things:
int (*afp2[N])(); // array of N ptr to func that returns int
Things are starting to get unwieldy here, so it’s time to reach for typedef.
typedef int (*FP)(); // ptr to func that returns int
FP afp3[N]; // array of N FP, same type as afp2
References to functions are rarely used and fill pretty much the same
niche as constant pointers to functions:
int (*const pFunc)(double) = aFunc; // const ptr to func
Often you’ll need something that behaves like a function pointer, but
function pointers tend to be unwieldy, dangerous, and (let’s admit it)
passé. Often the best approach is to use a function object instead of a
function pointer.
A function object, like a smart pointer (see Smart Pointers [42, 145]) is an
ordinary class object. Whereas a smart pointer type overloads the -> and
* (and possibly ->*) operators to mimic a “pointer on steroids,” a func-
tion object type overloads the function call operator, (), to create a
“function pointer on steroids.” Consider the following function object
that computes the next element in the well-known Fibonacci series (1, 1,
2, 3, 5, 8, 13, …) with each call:
class Fib {
public:
Fib() : a0_(1), a1_(1) {}
int operator ();
private:
int a0_, a1_;
};
int Fib::operator () {
int temp = a0_;
a0_ = a1_;
a1_ = temp + a0_;
return temp;
}
A function object is just a regular class object, but you can call its operator
() member (or members, if there is more than one) with standard function
call syntax.
Fib fib;
//...
cout << "next two in series: " << fib()
63
64 ❘ Item 18 Function Objects
It’s also possible and common to create the effect of a virtual function
pointer by creating a function object hierarchy with a virtual operator ().
Consider a numeric integration facility that calculates an approximation
of the area under a curve, as shown in Figure 5.
low high
This works, but it’s inflexible because it uses a function pointer to indicate
the function to be integrated; it can’t handle functions that require state
or pointers to member functions. An alternative is to create a function
object hierarchy. The base of the hierarchy is a simple interface class that
declares a pure virtual operator ().
class Func {
public:
virtual ~Func();
virtual double operator ()( double ) = 0;
};
double integrate( Func &f, double low, double high );
67
68 ❘ Item 19 Commands and Hollywood
//...
};
A user of a Button would set a callback function and then hand the Button
over to framework code that could detect when the Button is clicked and
execute the action.
extern void playMusic();
//...
Button *b = new Button( "Anoko no namaewa" );
b->setAction( playMusic );
registerButtonWithFramework( b );
A Button can now work with any function object that is-a Action, like
this one:
class PlayMusic : public Action {
public:
PlayMusic( const string &songFile )
: song_(songFile) {}
void operator ()(); // plays the song
//...
private:
MP3 song_;
};
70 ❘ Item 19 Commands and Hollywood
The encapsulated data (in this case, the song to play) preserves both the
flexibility and safety of the PlayMusic function object.
Button *b = new Button( "Anoko no namaewa" );
auto_ptr<PlayMusic>
song( new PlayMusic( "AnokoNoNamaewa.mp3" ) );
b->setAction( song.get() );
How did we ever get by without the STL? Not only is it easier and faster to
write complex code, but that code is both standard and highly optimized.
std::vector<std::string> names;
//...
std::sort( names.begin(), names.end() );
Another nice thing about the STL is that it’s highly configurable. In the
code above, we used string’s less-than operator to sort a vector of
strings, but we don’t always have a less-than operator to work with, or we
may not want to sort in ascending order.
class State {
public:
//...
int population() const;
float aveTempF() const;
//...
};
The State class (which represents a state of the union) doesn’t have a
less-than operator, and we probably don’t want to implement one
because it’s not clear what it would mean for one state to be less than
another (do we compare names, population, percentage of elected offi-
cials under indictment, …?). Fortunately, the STL generally allows us to
specify an alternate less-than-like operation in situations like this. Such
an operation is called a “comparator,” because it compares two values:
inline bool popLess( const State &a, const State &b )
{ return a.population() < b.population(); }
71
72 ❘ Item 20 STL Function Objects
Note the parentheses that follow PopLess in this call to sort. PopLess is
a type, but we have to pass an object of that type as a function argument.
By appending parentheses to the PopLess type name, we create an
unnamed temporary PopLess object that exists for the duration of the
function call. (These nameless objects are known as “anonymous tempo-
raries,” a term I’ve always enjoyed because it sounds vaguely racy.) We
could have declared and passed a named object:
PopLess comp;
sort( states, states+50, comp );
The design guidelines for STL predicates are identical to those for STL
comparators with the exception, of course, that they’re unary rather than
binary functions. Starting with our previous sorted State results, the
appropriate predicate makes it trivial to find a warm place without too
many dang people:
State *warmandsparse = find_if( states, states+50, IsWarm() );
❘
Item 21 Overloading and Overriding
Are Different
75
76 ❘ Item 21 Overloading and Overriding Are Different
The member function D::f(int) overrides the base class virtual function
B::f(int). The member function D::f(B *) doesn’t override anything,
because B::f(B *) is not virtual. It does, however, overload D::f(int).
Note that it does not overload the base class members B::f, because it’s in
a different scope (see also Optional Keywords [63, 231]).
Overloading and overriding are different concepts, and a technical under-
standing of their differences is essential if you want to grok advice on
advanced base class interface design.
❘
Item 22 Template Method
77
78 ❘ Item 22 Template Method
Global scope was getting overly crowded. Everybody and his brother
implemented libraries that reused the same names for different classes
and functions. For example, many libraries wanted to include a class
named String, but if you used two different libraries that defined a
String type, you’d get a multiple definition error or worse. Various extra-
language approaches used to address this problem (naming conventions,
the preprocessor, …) only made things worse. Namespaces to the rescue.
In some ways, namespaces introduce complexity (see Argument Dependent
Lookup [25, 89]), but most uses of namespaces are simple and simplifying.
A namespace is a subdivision of global scope:
namespace org_semantics {
class String { ... };
String operator +( const String &, const String & );
// other classes, functions, typedefs, etc...
}
81
82 ❘ Item 23 Namespaces
This has the advantage of not allowing one to inadvertently declare a new
namespace name (as we did when we left out the const in the second
argument of our first definition of operator +) rather than define an
already declared one. Admittedly, the seemingly endless repetition of
org_semantics in this case can be tedious, but that’s the price of security!
We’ll discuss some approaches that can improve this situation.
If you want to use a name that’s defined in a particular namespace, you
have to tell the compiler in what namespace the name is to be found:
org_semantics::String s( "Hello, World!" );
Although some of the C++ standard library has remained in global scope
(the standard global operator news, operator deletes, array news, and
array deletes come to mind), the bulk of the standard library is now resi-
dent in the std (that is, “standard”) namespace, and most standard
library use requires qualification with the std namespace name:
#include <iostream>
#include <vector>
//...
void aFunc() {
vector<int> a; // error! I don't see a vector!
std::vector<int> b; // Oh, there it is!
cout << "Oops!" << endl; // errors!
std::cout << "Better!" << std::endl; // OK
//...
}
That’s a bad idea. Now we’re back nearly to square one, with all the names
from a namespace available everywhere, sowing confusion and disarray.
This is a particularly bad idea in a header file, where you can leverage your
bad decision over any file that includes your header. In header files, we
usually prefer to stick with explicit qualification and reserve using direc-
tives for smaller scopes (such as function bodies or blocks within a func-
tion) where their effects are bounded and easier to control. Basically, you
have to be on your best behavior in header files and on pretty good behav-
ior in source files, but you can kick back and relax inside a function.
One interesting aspect of using directives is that they make the names of a
namespace available, but as if they were declared at global scope, not nec-
essarily at the scope in which the using directive occurs. Local names will
hide namespace names:
void aFunc() {
using namespace std; // using directive
//...
int vector = 12; // a poorly named local variable...
vector<int> a; // error! std::vector is hidden
std::vector<int> b; // OK, can use explicit qualification
//...
}
84 ❘ Item 23 Namespaces
This is the trendy new way to avoid declaring functions and variables with
static linkage. In the anonymous namespace above, both anInt and
aFunc have external linkage, but they can be accessed only within the
translation unit (that is, preprocessed file) in which they occur, just like
a static.
This page intentionally left blank
❘
Item 24 Member Function Lookup
When you call a member function, there are three steps involved. First,
the compiler looks up the name of the function. Second, it chooses the
best matching function from the available candidates. Third, it checks
that you have access to the matched function. That’s it. Admittedly, each
of these steps (especially the first two; see Argument Dependent Lookup
[25, 89] and Operator Function Lookup [26, 91]) can be complex, but the
overall function matching mechanism is as simple as one, two, three.
Most errors related to function matching stem not from misunderstand-
ing the compiler’s complex name lookup and overloaded function
matching algorithms but from misunderstanding the simple, sequential
nature of these three steps. Consider the following:
class B {
public:
//...
void f( double );
};
class D : public B {
void f( int );
};
//...
D d;
d.f( 12.3 ); // confusing
87
88 ❘ Item 24 Member Function Lookup
89
90 ❘ Item 25 Argument Dependent Lookup
In this case, the first (leftmost) use of operator << is most probably a call
of a member function of the class template std::basic_ostream
whereas the second is a non-member function call of an overloaded
operator << function in the org_semantics namespace. These details
are really of no interest to the author of the greeting, and ADL sorts things
out rather nicely.
❘
Item 26 Operator Function Lookup
When we use the function call syntax, the usual lookup rules apply (see
Member Function Lookup [24, 87]), and the call b.operator %(c) is
treated in the same way as the similar call to memFunc1. However, an infix
call of an overloaded operator is handled differently:
X operator %( const X &, int ); // non-member operator
//...
void X::memFunc2() {
*this % 12; // calls non-member operator %
operator %( *this, 12 ); // error! too many arguments
}
91
92 ❘ Item 26 Operator Function Lookup
For an infix operator call, the compiler will consider both member and
non-member functions (see also Argument Dependent Lookup [25, 89]),
so the first, infix call to operator % will match the non-member. This is
not an instance of overloading; it’s a question of the compiler looking in
two different places for candidate functions. The second, non-infix call of
operator % follows the standard function lookup rules and finds the
member function. We have an error because we are attempting to pass
three arguments to a binary function. (Remember the implicit this argu-
ment for member functions!)
In effect, infix calls of overloaded operators implement a kind of “degen-
erate” ADL in which both the class of the left (or only) argument of the
infix operator and the global scope are considered when determining
what functions will be considered for overload resolution. ADL extends
this process to include candidate operator functions in other namespaces
brought in by the arguments of the operator. Note that this is not over-
loading. Overloading is a static property of a function declaration (see
Overloading and Overriding Are Different [21, 75]). Both ADL and infix
operator function lookup are properties of the arguments supplied to a
function call.
❘
Item 27 Capability Queries
Most times when an object shows up for work, it’s capable of performing
as required, because its capabilities are advertised explicitly in its inter-
face. In these cases, we don’t ask the object if it can do the job; we just tell
it to get to work:
class Shape {
public:
virtual ~Shape();
virtual void draw() const = 0;
//...
};
//...
Shape *s = getSomeShape(); // get a shape, and tell it to...
s->draw(); // ...get to work!
Even though we don’t know precisely what type of shape we’re dealing
with, we know that it is-a Shape and, therefore, can draw itself. This is a
simple and efficient—and therefore desirable—state of affairs.
However, life is not always that straightforward. Sometimes an object
shows up for work whose capabilities are not obvious. For example, we
may have a need for a shape that can be rolled:
class Rollable {
public:
virtual ~Rollable();
virtual void roll() = 0;
};
93
94 ❘ Item 27 Capability Queries
capable of doing. In this case, we’re saying that anything that is-a Rollable
can roll. Some shapes can roll; others can’t:
class Circle : public Shape, public Rollable { // circles roll
//...
void draw() const;
void roll();
//...
};
class Square : public Shape { // squares don't
//...
void draw() const;
//...
};
Ideally, our code should be partitioned in such a way that we always know
whether we are dealing with objects that are Rollable before we attempt
to roll them, just as we earlier knew we were dealing with Shapes before
we attempted to draw them.
vector<Rollable *> rollingStock;
//...
for( vector<Rollable *>::iterator i( rollingstock.begin() );
i != rollingStock.end(); ++i )
(*i)->roll();
Shape Rollable
Square Circle
Shape *s = getSomeShape();
if( Rollable *roller = dynamic_cast<Rollable *>(s) )
roller->roll();
In C++, an object can have multiple, valid addresses, and pointer com-
parison is not a question about addresses. It’s a question about object
identity.
class Shape { ... };
class Subject { ... };
class ObservedBlob : public Shape, public Subject { ... };
if( ob == s ) ...
if( subj == ob ) ...
In this case, both of these conditions will be true even if the addresses
contained in ob, s, and subj differ. Consider two possible memory lay-
outs for the ObservedBlob object to which these pointers refer, as shown
in Figure 7.
Under layout #1, s and subj refer to Shape and Subject subobjects
within the complete object that have different addresses from the com-
plete object to which ob refers. Under layout #2, the Shape subobject hap-
pens to have the same address as the ObservedBlob complete object, so
ob and s contain the same address.
97
98 ❘ Item 28 Meaning of Pointer Comparison
ob
ObservedBlob ObservedBlob
Layout #1 Layout #2
Figure 7 ❘ Two possible layouts for an object under multiple inheritance. Under either
layout, the object has multiple addresses.
Under either layout, ob, s, and subj refer to the same ObservedBlob
object, so the compiler must ensure that ob compares equal to both s and
subj. (We can’t compare s with subj because they have no inheritance
relationship.) The compiler accomplishes this comparison by adjusting
the value of one of the pointers being compared by the appropriate offset.
For example, the expression
ob == subj
Once we’ve stripped the address contained in subj of its type informa-
tion by copying it to a void *, the compiler has no choice but to fall back
on raw address comparison, and with pointers to class objects that’s rarely
appropriate.
❘
Item 29 Virtual Constructors
and Prototype
Suppose you find yourself in a Swedish restaurant, and you’d like to order
a meal. Unfortunately, your knowledge of Swedish is limited to technical
correspondence, cursing, or (typically) a combination of the two. The
menu is in Swedish, and you can’t read Swedish, but you do notice a gen-
tleman on the other side of the room who is really enjoying his meal.
Therefore, you call over the waiter and say
If that gentleman is having fish, I’d like fish. If he’s having spaghetti, I’d
like spaghetti too. Otherwise, if he’s having eel, then eel it is. However, if
he has decided on the preserved kumquats, then I’ll have those.
Does this sound reasonable? Of course not. (For one thing, you probably
don’t want to order spaghetti in a Swedish restaurant.) This procedure
has two basic problems. First, it’s tedious and inefficient. Second, it can
fail. What would happen if you come to the end of your sequence of ques-
tions and you still haven’t been able to guess what the other diner is eating?
The waiter will walk off, leaving you stranded and hungry. Even if you
happen to know the entire content of the menu and are therefore guaran-
teed of (eventual) success, your list of questions may become invalid or
incomplete if the menu is modified between your visits to the restaurant.
The proper approach, of course, is simply to call the waiter over and say,
“I’d like what that gentleman is having.”
If the waiter is a literalist, he’ll snatch up the other diner’s half-finished
meal and bring it to your table. However, that sort of behavior can lead to
hurt feelings and even a food fight. This is the sort of unpleasantness that
can occur when two diners try to consume the same meal at the same
time. If he knows his business, the waiter will deliver an exact copy of
the other diner’s meal to you, without affecting the state of the meal that
is copied.
99
100 ❘ Item 29 Virtual Constructors and Prototype
These are the two major reasons for cloning: You must (or you prefer to)
remain in ignorance about the precise type of object you’re dealing with,
and you don’t want to effect change or be affected by changes to the origi-
nal object.
A member function that provides the ability to clone an object is tradi-
tionally called a “virtual constructor” in C++. Of course, there are no vir-
tual constructors, but producing a copy of an object generally involves
indirect invocation of its class’s copy constructor through a virtual
function, giving the effect—if not the reality—of virtual construction.
More recently, this technique has been called an instance of the Proto-
type pattern.
Of course, we have to know something about the object to which we refer.
In our case, we know that what we want is-a meal.
class Meal {
public:
virtual ~Meal();
virtual void eat() = 0;
virtual Meal *clone() const = 0;
//...
};
The Meal type provides the ability to clone with the clone member func-
tion. The clone function is actually a specialized kind of Factory Method
(see Factory Method [30, 103]) that manufactures an appropriate product
while allowing the invoking code to remain in ignorance of the exact type
of context and product class. Concrete classes derived from Meal (that is,
those meals that actually exist and are listed on the menu) must provide
an implementation of the pure virtual clone operation.
class Spaghetti : public Meal {
public:
Spaghetti( const Spaghetti & ); // copy ctor
void eat();
Spaghetti *clone() const
{ return new Spaghetti( *this ); } // call copy ctor
//...
};
Item 29 Virtual Constructors and Prototype ❘ 101
(For an explanation as to why the return type of the overriding derived
class clone function differs from that of the base class function, see
Covariant Return Types [31, 107].)
With this simple framework in place, we have the ability to produce a
copy of any type of Meal without precise knowledge about the actual type
of the Meal we’re copying. Note that the following code has no mention
of concrete derived classes and therefore no coupling of the code to any
current or future types derived from Meal.
const Meal *m = thatGuysMeal(); // whatever he's having...
Meal *myMeal = m->clone(); // ...I want one too!
In fact, we could end up ordering something we’ve never even heard of. In
effect, with Prototype, ignorance of the existence of a type is no barrier to
creating an object of that type. The polymorphic code above can be com-
piled and distributed, and later augmented with new types of Meal with-
out the need for recompilation.
This example illustrates some of the advantages of ignorance in software
design, particularly in the design of software structured as a framework
that is designed for customization and extension: The less you know, the
better.
This page intentionally left blank
❘
Item 30 Factory Method
Employee HRInfo
Figure 8 ❘
Pseudoparallel hierarchies. How should we map an employee to its
corresponding human resources information?
103
104 ❘ Item 30 Factory Method
One common approach that is always wrong is to use a type code and a
switch-statement:
class Employee {
public:
enum Type { SALARY, HOURLY, TEMP };
Type type() const { return type_; }
//...
private:
Type type_;
//...
};
//...
HRInfo *genInfo( const Employee &e ) {
switch( e.type() ) {
case SALARY:
case HOURLY: return new StdInfo( e );
case TEMP:return new TempInfo( static_cast<const Temp*>(e) );
default: return 0; // unknown type code!
}
}
We still have a problem in that we may not know that we are dealing with
a Temp employee rather than some other type of employee. But that’s easy
to fix with a virtual function:
class Employee {
public:
//...
virtual HRInfo *genInfo() const = 0; // Factory Method
//...
};
This is an instance of the Factory Method pattern. Rather than ask a series
of blunt personal questions of an employee, we are, in effect, saying,
“Whatever type of employee you are, generate the appropriate type of
information for yourself.”
Employee *e = getAnEmployee();
//...
HRInfo *info = e->genInfo(); // use Factory Method
106 ❘ Item 30 Factory Method
The essence of Factory Method is that the base class provides a virtual
function hook for generating an appropriate “product.” Each derived class
may override that inherited virtual function to generate an appropriate
product for itself. In effect, we have the ability to use an object of unknown
type (“some type of employee”) to generate an object of unknown type
(“the appropriate type of information”).
Use of a Factory Method is often indicated when a high-level design
requires generation of the “appropriate” object based on the exact type of
another object, in the case of parallel or almost parallel hierarchies, and is
often the cure for a series of runtime type queries.
❘
Item 31 Covariant Return Types
Generally, an overriding function must have the same return type as the
function it overrides:
class Shape {
public:
//...
virtual double area() const = 0;
//...
};
class Circle : public Shape {
public:
float area() const; // error! different return type
//...
};
However, this rule is relaxed for what are known as “covariant return
types.” If B is a class type, and a base class virtual function returns B *,
then an overriding derived class function may return D *, where D is pub-
licly derived from B. (That is, D is-a B.) If a base class virtual function
returns B &, then an overriding derived class function may return D &.
Consider the following clone operation on a shape hierarchy (see Virtual
Constructors and Prototype [29, 99]):
class Shape {
public:
//...
virtual Shape *clone() const = 0; // Prototype
//...
};
class Circle : public Shape {
public:
Circle *clone() const;
//...
};
107
108 ❘ Item 31 Covariant Return Types
It’s necessary to declare the copy constructor and copy assignment opera-
tor, since otherwise the compiler would declare them implicitly, as public
inline members. By declaring them to be private, we forestall the com-
piler’s meddling and ensure that any use of the operations—whether
explicit or implicit—will result in a compile-time error:
void aFunc( NoCopy );
void anotherFunc( const NoCopy & );
NoCopy a( 12 );
NoCopy b( a ); // error! copy ctor
NoCopy c = 12; // error! implicit copy ctor
a = b; // error! copy assignment
aFunc( a ); // error! pass by value with copy ctor
aFunc( 12 ); // error! implicit copy ctor
anotherFunc( a ); // OK, pass by reference
anotherFunc( 12 ); // OK
111
This page intentionally left blank
❘
Item 33 Manufacturing Abstract Bases
113
114 ❘ Item 33 Manufacturing Abstract Bases
The constructors are declared protected to allow their use by derived class
constructors, while preventing the creation of standalone ABC objects.
void func1( ABC );
void func2( ABC & );
ABC a; // error! protected default ctor
D d; // OK
func1( d ); // error! protected copy ctor
func2( d ); // OK, no copy ctor
Another way to coerce a class into being an abstract base class is to bite
the bullet and designate one of its virtual functions as pure. Often the
destructor is a good choice:
class ABC {
public:
virtual ~ABC() = 0;
//...
};
//...
ABC::~ABC() { ... }
The operator new and operator delete members are defined (as well
as declared) because they may be called implicitly from constructors and
destructors on some platforms. They are declared to be protected for the
117
118 ❘ Item 34 Restricting Heap Allocation
same reason; they may be invoked implicitly from derived class construc-
tors and destructors. If NoHeap is not intended for use as a base class,
these functions may be declared to be private.
At the same time, we may also want to pay attention to allocation of
arrays of NoHeap objects (see Array Allocation [37, 127]). In this case, we
can simply declare array new and array delete to be private and unde-
fined, similar to the way we deny copy operations (see Preventing Copying
[32, 111]).
class NoHeap {
public:
//...
protected:
void *operator new( size_t ) { return 0; }
void operator delete( void * ) {}
private:
void *operator new[]( size_t );
void operator delete[]( void * );
};
It’s impossible to call a constructor directly. However, we can trick the com-
piler into calling a constructor for us through the use of placement new.
void *operator new( size_t, void *p ) throw()
{ return p; }
It’s important to distinguish the new operator from functions that are
named operator new. The new operator can’t be overloaded and so
always behaves in the same way; it calls a function named operator new
and then initializes the returned storage. Any variation of behavior we
achieve with memory allocation has to do with different, overloaded ver-
sions of operator new, not with the new operator. The same applies to
the delete operator and operator delete.
Placement new is a version of the function operator new that doesn’t
actually allocate any storage; it just returns a pointer to some storage that
(presumably) is already allocated. Because no storage is allocated by a call
to placement new, it’s important not to delete it.
delete com1; // oops!
119
120 ❘ Item 35 Placement New
One potential problem with arrays of class objects is that each element
must be initialized by a call to a default constructor when the array is allo-
cated. Consider a simple, fixed-size buffer to which one can append a new
value:
string *sbuf = new string[BUFSIZ]; // BUFSIZ default ctor calls!
int size = 0;
void append( string buf[], int &size, const string &val )
{ buf[size++] = val; } // wipe out default initialization!
This approach is fast, clever, and not intended for viewing by the general
public. This basic technique is used extensively (in a more sophisticated
form) in most implementations of the standard library containers.
This page intentionally left blank
❘
Item 36 Class-Specific Memory
Management
If you don’t like the way standard operator new and operator delete
are treating one of your class types, you don’t have to stand for it. Instead,
your types can have their own operator new and operator delete cus-
tomized to their needs.
Note that we can’t do anything with the new operator or the delete
operator, since their behavior is fixed, but we can change which operator
new and operator delete they invoke (see Placement New [35, 119]). The
best way to do this is to declare member operator new and operator
delete functions:
class Handle {
public:
//...
void *operator new( size_t );
void operator delete( void * );
//...
};
//...
Handle *h = new Handle; // uses Handle::operator new
//...
delete h; // uses Handle::operator delete
Member operator new and operator delete are static member func-
tions (see Optional Keywords [63, 231]), which makes sense. Recall that
123
124 ❘ Item 36 Class-Specific Memory Management
static member functions have no this pointer, and these functions are
charged with simply getting and releasing the storage for an object, so
they have no use for a this pointer. Like other static member functions,
they are inherited by derived classes:
class MyHandle : public Handle {
//...
};
//...
MyHandle *mh = new MyHandle; // uses Handle::operator new
//...
delete mh; // uses Handle::operator delete
Of course, if MyHandle had declared its own operator new and operator
delete, those would have been found first by the compiler during
lookup, and they would have been used instead of the inherited versions
from the Handle base class.
If you define member operator new and operator delete in a base
class, ensure that the base class destructor is virtual:
class Handle {
public:
//...
virtual ~Handle();
void *operator new( size_t );
void operator delete( void * );
//...
};
class MyHandle : public Handle {
//...
void *operator new( size_t );
void operator delete( void *, size_t ); // note 2nd arg
//...
};
//...
Handle *h = new MyHandle; // uses MyHandle::operator new
//...
delete h; // uses MyHandle::operator delete
Item 36 Class-Specific Memory Management ❘ 125
Without a virtual destructor, the effect of deleting a derived class object
through a base class pointer is undefined! The implementation may simply
(and probably incorrectly) invoke Handle::operator delete rather
than MyHandle::operator delete, but anything at all could happen.
Notice also that we’ve employed a two-argument version of operator
delete rather than the usual one-argument version. This two-argument
version is another “usual” version of member operator delete often
employed by base classes that expect derived classes to inherit their
operator delete implementation. The second argument will contain
the size of the object being deleted—information that is often useful in
implementing custom memory management.
A common misconception is that use of the new and delete operators
implies use of the heap (or freestore) memory, but this is not the case. The
only implication in using the new operator is that a function called
operator new will be called and that function will return a pointer to
some memory. The standard, global operator new and operator
delete do indeed allocate memory from the heap, but member operator
new and operator delete can do whatever they like. There is no restric-
tion as to where that memory comes from; it may come from a special
heap, from a statically allocated block, from the guts of a standard con-
tainer, or from a block of storage local to a function. The only limit to
where the memory comes from is your creativity and common sense. For
example, Handle objects could be allocated from a static block like this:
struct rep {
enum { max = 1000 };
static rep *free; // head of freelist
static int num_used; // number of slots used
union {
char store[sizeof(Handle)];
rep *next;
};
};
static rep mem[ rep::max ]; // block of static storage
void *Handle::operator new( size_t ) {
if( rep::free ) { // if something on freelist
rep *tmp = rep::free; // take from freelist
rep::free = rep::free->next;
return tmp;
126 ❘ Item 36 Class-Specific Memory Management
}
else if( rep::num_used < rep::max ) // if slots left
return &mem[ rep::num_used++ ]; // return unused slot
else // otherwise, we're...
throw std::bad_alloc(); // ...out of memory!
}
void Handle::operator delete( void *p ) { // add to freelist
static_cast<rep *>(p)->next = rep::free;
rep::free = static_cast<rep *>(p);
}
Most C++ programmers know to keep the array and nonarray forms
straight when allocating and deallocating memory.
T *aT = new T; // non-array
T *aryT = new T[12]; // array
delete [] aryT; // array
delete aT; // non-array
aT = new T[1]; // array
delete aT; // error! should be array
The reason it’s important to keep these functions properly paired is that
array allocation and deallocation use different functions from nonarray
allocation and deallocation. A new expression does not use operator new
to allocate storage for an array. It uses array new. Similarly, a delete
expression does not invoke operator delete to free an array’s storage; it
invokes array delete. To be precise, when you allocate an array, you’re
using a different operator (new[]) than you do when you allocate a
nonarray (new), and likewise for deallocation.
Array new and array delete are array analogs of operator new and oper-
ator delete and are declared similarly:
The most common source of confusion with the array forms of these
functions occurs when a particular class or hierarchy defines its own
memory management with member operator new and operator
delete (see Class-Specific Memory Management [36, 123]).
class Handle {
public:
//...
127
128 ❘ Item 37 Array Allocation
It’s also possible to call operator new directly, in which case we must
specify the number of bytes we want to allocate explicitly:
aT = static_cast<T *>(operator new( sizeof(T) ));
However, when we call array new implicitly through a new expression, the
compiler may, and often does, increase the memory request by a small
amount.
aryT = new T[5]; // request 5*sizeof(T) + delta bytes
First, note that exceptions are synchronous and can occur only at the
boundary of a function call. Therefore, operations like arithmetic on pre-
defined types, assignment of predefined types (especially pointers), and
other low-level operations will not result in an exception. (They could
result in a signal or interrupt of some sort, but these are not exceptions.)
Operator overloading and templates complicate this situation, since it’s
often difficult to determine whether a given operation will result in a
function call and potential exception. For example, if I assign character
pointers, I know I won’t get an exception, whereas if I assign user-defined
Strings, I might:
131
132 ❘ Item 38 Exception Safety Axioms
This is not necessary or advisable, since the fear of social ostracism also
(one hopes and assumes) affects the authors of the destructors and
operator deletes of the objects to which ptr1_ and ptr2_ refer. We can
play on those fears to make our task easier:
X::~X() {
delete ptr1_;
delete ptr2_;
}
Item 38 Exception Safety Axioms ❘ 133
Axiom 3: Swap Doesn’t Throw
This is another socially based axiom, but it’s not as ingrained and univer-
sally recognized as the prohibition on destructors and deletions that
throw. Swapping would not seem to be a very common operation, but it is
used extensively “behind the scenes,” most particularly in STL implemen-
tations. Whenever you perform a sort, reverse, partition, or any of a
large number of other operations, you’re swapping, and an exception safe
swap goes a long way toward guaranteeing that these operations will be
exception safe as well. See also Copy Operations [13, 45].
This page intentionally left blank
❘
Item 39 Exception Safe Functions
The hard part about writing exception safe code isn’t the throwing or
catching of exceptions; it’s everything in between. As a thrown exception
wends its way from the throw expression to the catch clause, every par-
tially executed function on that path must “clean up” any important
resources that it controls before its activation record is popped off the
execution stack. Generally (but not always), all that is required to write an
exception safe function is a moment’s reflection and some common
sense.
For example, consider the implementation of String assignment from
Assignment and Initialization Are Different [12, 41]:
String &String::operator =( const char *str ) {
if( !str ) str = "";
char *tmp = strcpy( new char[ strlen(str)+1 ], str );
delete [] s_;
s_ = tmp;
return *this;
}
delete [] s_;
if( !str ) str = "";
s_ = strcpy( new char[ strlen(str)+1 ], str );
return *this;
}
However, while the array delete comes with a social guarantee not to
throw an exception (see Exception Safety Axioms [38, 131]), the array new
a couple of lines later makes no such promise. If we delete the old buffer
135
136 ❘ Item 39 Exception Safe Functions
before we know whether allocation of the new buffer will succeed, we’ll
leave the String object in a bad state. Herb Sutter summarizes the situa-
tion well in his Exceptional C++, which I’ll paraphrase as this: First do
anything that could cause an exception “off to the side” without changing
important state, and then use operations that can’t throw an exception
to finish up. That’s what we did in the first implementation of
String::operator = above. Let’s look at another example from
Commands and Hollywood [19, 67]:
void Button::setAction( const Action *newAction ) {
Action *temp = newAction->clone(); // off to the side...
delete action_; // then change state!
action_ = temp;
}
Because it’s a virtual function, we really know nothing about the excep-
tion-related behavior of the call to clone, so we assume the worst. If the
clone operation succeeds, we continue with an exception safe deletion
and pointer assignment. Otherwise, a thrown exception from clone will
cause premature exit from Button::setAction with no harm done.
Newer C++ programmers have a tendency to “clean up” code like this in
such a way as to make it exception unsafe:
void Button::setAction( const Action *newAction ) {
delete action_; // change state!
action_ = newAction->clone(); // then maybe throw?
}
This version with its fussy try block and catch clause is exception safe in
the sense that the Button object is left in a consistent (but likely different)
state if clone throws an exception. However, our previous version was
shorter, simpler, and more exception safe because it left the Button object
not merely consistent but unchanged.
It’s a good idea to minimize the use of try blocks, where possible, and
employ them primarily in locations where you really want to examine the
type of a passing exception in order to do something with it. In practice,
these locations are often at module boundaries between your code and
third-party libraries and between your code and the operating system.
This page intentionally left blank
❘
Item 40 RAII
139
140 ❘ Item 40 RAII
It may be that the original version of this function was safe, and the
resource to which rh referred was always recovered. However, over time
such code tends to break, as less experienced maintainers insert early
returns, call functions that can throw an exception, or otherwise avoid the
resource recovery code at the end of the function. Use of RAII results in a
function that is both simpler and more robust:
void f() {
ResourceHandle rh( new Resource );
//...
if( iFeelLikeIt() ) // no problem!
return;
//...
g(); // exception? no problem!
// rh destructor performs deletion!
}
The only time you’re not guaranteed a destructor call when using RAII is
if the resource handle is allocated on the heap, since the destructor will be
called only if the object is deleted explicitly. (In the interests of full disclo-
sure, there are also edge cases where abort or exit is called and an iffy
situation that can occur if a thrown exception is never caught.)
ResourceHandle *rhp =
new ResourceHandle(new Resource); // bad idea!
Item 40 RAII ❘ 141
RAII is such a pervasive technique in C++ programming that it’s hard to
find a library component or significant block of code that does not
employ it in some fashion. Note that we have a very broad definition of
“resource” that can be controlled via RAII. In addition to resources that
are essentially hunks of memory (buffers, strings, container implementa-
tions, and the like), we can use RAII to control system resources like file
handles, semaphores, and network connections, as well as less glamorous
things like login sessions, graphical shapes, or zoo animals.
Consider the following class:
class Trace {
public:
Trace( const char *msg ) : msg_(msg)
{ std::cout << "Entering " << msg_ << std::endl; }
~Trace()
{ std::cout << "Leaving " << msg_ << std::endl; }
private:
std::string msg_;
};
To write perfectly exception safe code, it’s necessary to keep track of any
allocated resources and to be prepared to release them if an exception
occurs. This is often a straightforward process. We can either organize our
code in such a way that no resource recovery is necessary (see Exception
Safe Functions [39, 135]) or use resource handles to recover the resources
automatically (see RAII [40, 139]). In extreme situations, we can get down
and dirty with try blocks or even nested try blocks, but this should be an
exception, not the rule.
We do, however, have an apparent problem with the use of the new
operator. The new operator actually performs two separate operations
(see Placement New [35, 119]); first it calls a function named operator
new to allocate some storage, and then it may invoke a constructor to turn
that uninitialized storage into an object:
String *title = new String( "Kicks" );
143
144 ❘ Item 41 New, Constructors, and Exceptions
}
catch( ... ) {
::operator delete( title ); // clean up if ctor throws
}
Ouch. So many things are wrong with this code that the approach is not
worth considering. In addition to being more trouble for you, the over-
worked coder, it will not behave properly if String has a member
operator new and operator delete (see Class-Specific Memory Man-
agement [36, 123]). This is a perfect example of too-clever code that works
initially but fails subtly in the future because of a remote change (for
example, if someone adds String-specific memory management).
Fortunately, the compiler handles this situation for us and produces code
that performs in the same way as in our hand-coded approach above, but
with one exception. It will invoke the operator delete that corresponds
to the operator new used to perform the allocation.
String *title = new String( "Kicks" ); // use members if present
String *title = ::new String( "Kicks" ); // use global new/delete
In particular, if the allocation uses a member operator new, then the cor-
responding member operator delete will be called to reclaim the stor-
age if the String constructor throws an exception. This is yet another
good reason to declare a member operator delete if you declare a
member operator new.
Essentially the same situation applies to array allocation and allocations
that use overloaded versions of operator new[]; the compiler will
attempt to find and call the appropriate operator delete[].
❘
Item 42 Smart Pointers
145
146 ❘ Item 42 Smart Pointers
The key to this façade is the overloaded operator ->. The -> operator
must be overloaded as a member and has a rather unusual property in
that it is not “consumed” when it is called. In other words, when we write
s->draw(), the compiler recognizes that s is not a pointer but a class
object with an overloaded operator -> (that is, that s is a smart pointer).
This results in a call to the member overloaded operator, which returns
(in this case) a Shape * built-in pointer. This pointer is then used to call
Shape’s draw function. If you write this out longhand, you’ll get the fol-
lowing challenging expression: (s.operator ->())->draw(), which
contains two uses of the -> operator, one overloaded, one built in.
Smart pointers also typically overload operator * as well as operator ->
so that they may be used to refer to nonclass types.
CheckedPtr<int> ip = new int;
*ip = 12; // same as ip.operator *() = 12
(*s).draw(); // use on ptr to class, too
A third nice thing about auto_ptr is that it behaves like a built-in pointer
with respect to conversions:
auto_ptr<Circle> aCircle( new Circle );
aShape = aCircle;
147
148 ❘ Item 43 auto_ptr Is Unusual
Through its clever use of template member functions (see Member Tem-
plates [50, 173]) one auto_ptr can be copied to another if the correspon-
ding built-in pointers could. In the code above, an auto_ptr<Circle> can
be assigned to an auto_ptr<Shape> because a Circle * can be assigned to
a Shape * (assuming that Shape is a public base class of Circle).
Where auto_ptr differs from the typical smart pointer (or typical object,
for that matter) is in its copy operations. For a typical class, the copy oper-
ations (see Copy Operations [13, 45]) will not affect the source of the copy.
In other words, if T is some type
T a;
T b( a ); // copy construction of b with a
a = b; // assignment from b to a
This gives us an array and a pointer to somewhere near the middle of the
array, as shown in Figure 9.
If we increment or decrement the pointer curPoint, we are requesting
that it point to the next or previous short in the points array. In other
words, pointer arithmetic is always scaled in the size of the object that is
being pointed to; incrementing curPoint by one does not add a one byte
to the address in the pointer—it adds sizeof(short) bytes. This is why
there is no pointer arithmetic available on void * pointers; we don’t know
what type of object the void * refers to, so we can’t scale the arithmetic
properly.
points+4
curPoint-3
--curPoint ++curPoint
points:
curPoint-=2
149
150 ❘ Item 44 Pointer Arithmetic
The only time this simple scheme seems to cause confusion is in the case
of multidimensional arrays, because novice C++ programmers tend to
forget that a multidimensional array is an array of arrays:
const int ROWS = 2;
const int COLS = 3;
int table[ROWS][COLS]; // array of ROWS arrays of COLS ints
int (*ptable)[COLS] = table; // ptr to array of COLS ints
table:
iter
1st
1 2 3 4
❘
Figure 12 Possible implementation of a standard list. A list iterator isn’t a pointer,
but it is modeled on a pointer.
This page intentionally left blank
❘
Item 45 Template Terminology
template-parameter-list
template <>
template-id
class Heap < char *>;
//... template-argument-list
Heap<int > aHeap;
Heap<char *> aHeap2;
Figure 13 ❘
Template terminology. Precise use of terminology is essential to precise
communication of a template design.
153
154 ❘ Item 45 Template Terminology
155
156 ❘ Item 46 Class Template Explicit Specialization
T Heap<T>::pop() {
std::pop_heap( h_.begin(), h_.end() );
T tmp( h_.back() );
h_.pop_back();
return tmp;
}
This implementation works well for many types of values but falls down
in the case of pointers to characters. By default, the standard heap algo-
rithms use the < operator to compare and organize the elements in the
heap. In the case of pointers to characters, however, this would result in
the heap’s being organized according to the addresses of the strings to
which the character pointers refer, not the values of the strings them-
selves. That is, the heap will be organized by the values of the pointers, not
the values of what they point to.
We can address this particular difficulty with an explicit specialization of
the primary Heap template for pointers to character:
template <>
class Heap<const char *> {
public:
void push( const char *pval );
const char *pop();
bool empty() const { return h_.empty(); }
private:
std::vector<const char *> h_;
};
Note the absence of the template keyword and parameter list in the defi-
nition of Heap<const char *>::push; this is not a function template
because, as we noted above, the explicit specialization Heap<const char *>
is not a template.
With the availability of this complete specialization, we can distinguish
between Heaps of const char *s and other Heaps:
Heap<int> h1; // use primary template
Heap<const char *> h2; // use explicit specialization
Heap<char *> h3; // use primary template!
char *pop();
size_t size() const;
void capitalize();
// no empty!
private:
std::vector<char *> h_;
};
Let’s get it straight: you can’t partially specialize function templates. It’s
just not a part of the C++ language (although it may be some day). What
you probably want to do is overload them (see Overloading Function Tem-
plates [58, 213]). Accordingly, we are considering only class templates in
this item.
The way class template partial specialization works is straightforward. As
with complete specialization, you first need a general case—or primary
template—to specialize. Let’s use our Heap template from Class Template
Explicit Specialization [46, 155]:
template <typename T> class Heap;
Because the type (double *) does not match either of our character
pointer complete specializations, the compiler will instantiate the pri-
mary template. We could provide complete specializations for double *
and every other pointer type of interest, but this is onerous and ultimately
unmaintainable. This is a job for partial specialization:
template <typename T>
class Heap<T *> {
public:
void push( const T *val );
T *pop();
161
162 ❘ Item 47 Template Partial Specialization
Now let’s use our comparator to implement a push operation with the
correct behavior:
template <typename T>
void Heap<T *>::push( T *pval ) {
if( pval ) {
h_.push_back(pval);
std::push_heap( h_.begin(), h_.end(), PtrCmp<T>() );
}
}
The complete set of rules for choosing among the various available partial
specializations is rather involved, but most cases are straightforward.
Generally, the most specific, most restricted candidate is chosen. The par-
tial specialization mechanism is precise and allows us to select among
candidates with high precision. For example, we could augment our set of
partial specializations with one for pointers to const:
template <typename T>
class Heap<const T *> {
//...
};
//...
Heap<const int *> h6; // different partial spec, now T is int
A partial specialization of Heap must also take a single type name tem-
plate argument, and its template parameter list may have a single type
name parameter in its template header
template <typename T> class Heap<T *>;
With the addition of these partial specializations, we can special case for
Heaps of pointers to non-member functions that take two arguments and
Heaps of pointers to data members (though why you’d want heaps of
these things is anybody’s guess):
Heap<char *(*)(int,int)> h9; // partial spec
// R is char *, A1 and A2 are int
Heap<std::string Name::*> h10; // partial spec
// T is string, C is Name
❘
Item 48 Class Template Member
Specialization
Our complete specialization of Heap for const char * replaced the entire
implementation of the primary, even though its private implementation
165
166 ❘ Item 48 Class Template Member Specialization
and empty member function were perfectly adequate for a heap of char-
acter pointers. All we really had to do was specialize the push and pop
member functions:
template <>
void Heap<const char *>::push( const char *const &pval ) {
h_.push_back(pval);
std::push_heap( h_.begin(), h_.end(), strLess );
}
template<>
const char *Heap<const char *>::pop() {
std::pop_heap( h_.begin(), h_.end(), strLess );
const char *tmp = h_.back(); h_.pop_back();
return tmp;
}
Two final notes: First, other members of class templates may be explicitly
specialized in addition to member functions, including static members
and member templates.
Second, there is often confusion about the difference between explicit
specialization and explicit instantiation. As we’ve seen in this item,
explicit specialization is a means of providing a custom version of a tem-
plate or template member that differs from what one would have gotten
from an implicit instantiation. Explicit instantiation simply tells the com-
piler, explicitly, to instantiate a member that is identical to what one
would have gotten with an implicit instantiation.
template void Heap<double>::push( const double & );
Even experienced C++ programmers are often put off by the rather
complex syntax required to program with templates. Of all the syntactic
gyrations one has to undertake, none is more initially confusing than the
occasional need to help the compiler disambiguate a parse.
As an example, let’s look at a portion of an implementation of a simple,
nonstandard container template.
template <typename T>
class PtrList {
public:
//...
typedef T *ElemT;
void insert( ElemT );
//...
};
The nested name ElemT allows us easy access to what the PtrList template
considers to be its element type. Even though we instantiated PtrList with
the type name State, the element type is State *. In other circumstances,
PtrList could be implemented with a smart pointer element type, or a
very sophisticated implementation of PtrList might vary its implementa-
tion based on the properties of the type used to instantiate it (see Specializ-
ing for Type Information [52, 183]). Use of the nested type name helps to
insulate users of PtrList from these internal implementation decisions.
169
170 ❘ Item 49 Disambiguating with Typename
When the compiler encounters the qualified name T::ElemT, what does
it know? From the template parameter list it knows that T is a type name
of some sort. It can also determine that T is a class name because we’ve
employed the scope operator (::) to access a nested name of T. But that’s
all the compiler knows, because there is no information available about
the content of T. For instance, we could call aFuncTemplate with a
PtrList, in which case T::ElemT would be a type name.
PtrList<State> states;
//...
aFuncTemplate( states ); // T::ElemT is PtrList<State>::ElemT
Here we’ve used the typename keyword to tell the compiler explicitly that
the following qualified name is a type name. This allows the compiler to
parse the template correctly. Note that we are telling the compiler that
ElemT is a type name, not T. The compiler can already determine that T is
a type name. Similarly, if we were to write
typename A::B::C::D::E
we’d be telling the compiler that the (very) nested name E is a type name.
Of course, if aFuncTemplate is instantiated with a type that does not
satisfy the requirements of the parsed template, it will result in a compile-
time error.
struct Z {
// no member named ElemT...
};
Z aZ;
//...
aFuncTemplate( aZ ); // error! no member Z::ElemT
aFuncTemplate( anX ); // error! X::ElemT is not a type name
aFuncTemplate( states ); // OK. nested ElemT is a type
Class templates have members that are not themselves templates, and
many of these members can be defined outside the class. Let’s look at a
singly linked list container:
template <typename T>
class SList {
public:
SList() : head_(0) {}
//...
void push_front( const T &val );
void pop_front();
T front() const;
void reverse();
bool empty() const;
private:
struct Node {
Node *next_;
T el_;
};
Node *head_; // -> list
};
The member functions of a template, when defined outside the class tem-
plate, have a template header with the same structure as that used in the
class template definition:
template <typename T>
bool SList<T>::empty() const
{ return head_ == 0; }
173
174 ❘ Item 50 Member Templates
embed a truncated Node in the SList, rather than a pointer to a Node, but
this is sufficient for our needs here.) Generally, such a nested class type is
defined within the template itself, but it needn’t be:
template <typename T>
class SList {
public:
//...
private:
struct Node; // incomplete class declaration
Node *head_; // -> list
//...
};
The members empty and Node are examples of template members. But a
class template (or even a nontemplate class) can also have member tem-
plates. (Yes, we are witnessing yet another example of C++’s penchant
for defining easily confused technical terms. This little gem joins
with the new operator/operator new, covariance/contravariance, and
const_iterator/const iterator pairs to ensure that every design review
will be an adventure.) In the finest tautological tradition, a member tem-
plate is a member that is a template:
template <typename T>
class SList {
public:
//...
template <typename In> SList( In begin, In end );
//...
};
These template members can be used for copy constructor–like and copy
assignment–like operations.
176 ❘ Item 50 Member Templates
Here, we’ve instantiated two different versions of the sort member using
the standard function object types less and greater.
This page intentionally left blank
❘
Item 51 Disambiguating with Template
//...
};
The class template AnAlloc contains the nested name rebind, which is
itself a class template. It is used within the STL framework to create allo-
cators “just like” the allocator that was used to instantiate a container but
for a different element type. For example:
typedef AnAlloc<int> AI; // original allocator allocates ints
typedef AI::rebind<double>::other AD; // allocates doubles
typedef AnAlloc<double> AD; // legal! this is the same type
179
180 ❘ Item 51 Disambiguating with Template
It may look a little odd, but using the rebind mechanism allows one to
create a version of an existing allocator for a different element type with-
out knowing the type of the allocator or the type of the element.
typedef SomeAlloc::rebind<Node>::other NodeAlloc;
If the type name SomeAlloc follows the STL convention for allocators,
then it will have a nested rebind class template. Essentially, we’ve said,
“I don’t know what kind of allocator this type is, and I don’t know what it
allocates, but I want an allocator just like it that allocates Nodes!”
This level of ignorance can occur only within a template, where precise
types and values are not known until much later, when the template is
instantiated. Consider an augmentation of our SList container of Mem-
ber Templates [50, 173] to include an allocator type (A) that can allocate
elements (of type T). Like the standard containers, SList will provide a
default allocator argument:
template < typename T, class A = std::allocator<T> >
class SList {
//...
struct Node {
//...
};
typedef A::rebind<Node>::other NodeAlloc; // syntax error!
//...
};
As is typical for lists and other node-based containers, our list-of-T does
not actually allocate and manipulate Ts. Rather, it allocates and manipulates
nodes that contain a member of type T. This is the situation we described
above. We have some sort of allocator that knows how to allocate objects
of type T, but we want to allocate objects of type Node. However, when we
attempt to rebind, we get a syntax error.
Once again, the problem is that the compiler has no information about the
type name A at this point other than that it is a type name. The compiler
has to make the assumption that the nested name rebind is a nontem-
plate name, and the angle bracket that follows is parsed as a less-than. But
Item 51 Disambiguating with Template ❘ 181
our troubles are just beginning. Even if the compiler were somehow able
to determine that rebind is a template name, when it reached the (doubly)
nested name other, it would have to assume that it’s a nontype name!
Time for some clarification. The typedef must be written as follows:
typedef typename A::template rebind<Node>::other NodeAlloc;
The use of template tells the compiler that rebind is a template name,
and the use of typename tells the compiler that the whole mess refers to a
type name. Simple, right?
This page intentionally left blank
❘
Item 52 Specializing for Type
Information
Before we go on, I’d like to point out how simple the above code is, once
you get past its convoluted syntax. This is a simple example of what’s
known as template metaprogramming, that is, performing some por-
tion of a computation at compile time rather than runtime through the
use of template instantiation. It sounds high falutin’, but it still boils
down to an observation that might have come from one of my plainspoken,
cranberry-farming neighbors: “It’s an int if it’s an int.” Most advanced
C++ template programming is no more difficult than this, just more
involved.
With the primary template and complete specialization above, we can ask
(at compile time) whether an unknown type is actually an int:
template <typename X>
void aFunc( X &arg ) {
//...
183
184 ❘ Item 52 Specializing for Type Information
...IsInt<X>::result...
//...
}
The ability to ask such questions about types at compile time is the basis
of a number of important optimization and error-checking techniques.
Of course, knowing whether a particular type is precisely an int is of
limited utility. Knowing whether a type is a pointer is probably more gen-
erally useful, since implementations often take different forms depending
on whether they are dealing with pointers to objects or with objects
directly:
struct Yes {}; // a type analog to true
struct No {}; // a type analog to false
We’re asking a more general question with IsPtr than we did with
IsInt, so we’re employing partial specialization to “capture” the vari-
ously qualified versions of the pointer modifier. As advertised above, this
IsPtr facility is really no more difficult to understand than the IsInt
facility; it’s just more syntactically challenging. (See also SFINAE [59, 217]
for a similar metaprogramming technique.)
Item 52 Specializing for Type Information ❘ 185
To see the utility of the ability to ask questions about a type at compile
time, consider this implementation of a simple stack template:
template <typename T>
class Stack {
public:
~Stack();
void push( const T &val );
T &top();
void pop();
bool empty() const;
private:
//...
typedef std::deque<T> C;
typedef typename C::iterator I;
C s_;
};
However, we may have a problem with the Stack’s destructor. When the
Stack is destroyed, its deque data member will be destroyed as well,
which in turn will destroy any elements left in the deque. If these ele-
ments are pointers, however, the objects to which they point will not be
deleted; that’s just the way the standard deque container behaves. There-
fore, we have to decide on a pointer element deletion policy for our
Stack, which I will imperiously declare is to delete! (But see Policies [56, 205]
for a more flexible approach.) We can’t simply have the destructor delete
the deque elements, however, because that would cause an error in those
cases where the elements are not pointers.
One solution would be to use partial specialization of the Stack (primary)
template to handle stacks of pointers (see Template Partial Specialization
186 ❘ Item 52 Specializing for Type Information
[47, 161]). However, that seems like an overreaction when only a small
portion of the Stack’s behavior must change. A different approach sim-
ply asks the obvious question (at compile time) and acts accordingly: “If
the element type of the Stack is a pointer, then delete any remaining ele-
ments. Otherwise don’t delete.”
template <typename T>
class Stack {
public:
~Stack()
{ cleanup( typename IsPtr<T>::Result() ); }
//...
private:
void cleanup( Yes ) {
for( I i( s_.begin() ); i != s_.end(); ++i )
delete *i;
}
void cleanup( No )
{}
typedef std::deque<T> C;
typedef typename C::iterator I;
C s_;
};
189
190 ❘ Item 53 Embedded Type Information
Here we’ve asked the vector<int> to tell us what its iterator type is,
rather than make the assumption that it is int * (as it often is for many
implementations). The iterator type for vector<int> could just as well
be some other type (like a user-defined safe pointer type), so the only
portable way to write the loop above is to get the type of the iterator from
the vector<int> itself.
A more important observation is that this approach allows us to write
generic code that makes the assumption that the required information is
present.
template <class Container>
typename Container::Elem process( Container &c, int size ) {
Item 53 Embedded Type Information ❘ 191
typename Container::Temp temp
= typename Container::Elem();
for( int i = 0; i < size; ++i )
temp += c[i];
return temp;
}
This version of the process algorithm queries the Container type for its
personal information, and makes the assumption that Container defines
the nested type names Elem and Temp. (Note that we had to use the
typename keyword in three places to tell the compiler explicitly that the
nested names were type names and not some other kind of nested name.
See Disambiguating with Typename [49, 169].)
Strings strings;
aString = process( strings, strings.size() ); // OK
The process algorithm works well with our Seq container and will also
work with any other container that follows our convention.
template <typename T>
class ReadonlySeq {
public:
typedef const T Elem;
typedef T Temp;
//...
};
Sometimes it’s not enough to know just an object’s type. Often, there is
information related to the object’s type that is essential to working with
the object. In Embedded Type Information [53, 189], we saw how complex
types like the standard containers often embed information about them-
selves within themselves:
Strings strings;
aString = process( strings, strings.size() ); // OK
std::vector<std::string> strings2;
aString = process( strings2, strings2.size() ); // error!
extern double readings[RSIZ];
double r = process( readings, RSIZ ); // error!
The process algorithm works well with our Seq container but fails with
a standard vector container, because vector does not define the nested
type names that process assumes are present.
We can process a ReadonlySeq container because it validates our
assumptions, but we may also want to process containers that do not
follow our rather parochial convention, and we may want to process
container-like things that are not even classes. Traits classes are often used
to solve these problems.
A traits class is a collection of information about a type. Unlike our nested
container information, however, the traits class is independent of the type
it describes.
template <typename Cont>
struct ContainerTraits;
193
194 ❘ Item 54 Traits
With the addition of this traits class template, we have the choice of
referring to the nested Elem type of one of our container types either
through the container type or through the traits type instantiated with
the container type.
typedef Seq<int> Cont;
Cont::Elem e1;
ContainerTraits<Cont>::Elem e2; // same type as e1
template <>
struct ContainerTraits<const char *> {
typedef const char Elem;
typedef char Temp;
typedef const char *Ptr;
};
With this specialization in place for the “container” type const char *, we
can process an array of characters.
const char *name = "Arsene Lupin";
const char *r = process( name, strlen(name) );
We can continue in this fashion for other types of arrays, producing spe-
cializations for int *, const double *, and so on. However, it would be
more convenient to specify a single case for any type of pointer, since they
all will have similar properties. For this purpose, we employ partial spe-
cialization of the traits template for pointers:
template <typename T>
struct ContainerTraits<T *> {
typedef T Elem;
typedef T Temp;
typedef T *Ptr;
};
We’re not quite there yet, however. Notice that using the partial special-
ization for a pointer to constant will not result in the correct type for use
as a “temporary.” That is, constant temporary values are not of much use
because we can’t assign to them. What we’d like is to have the non-constant
analog of the element type as our temporary type. In the case of const
char *, for instance, ContainerTraits<const char *>::Temp should
Item 54 Traits ❘ 197
be char, not const char. We can handle this case with an additional
partial specialization:
template <typename T>
struct ContainerTraits<const T *> {
typedef const T Elem;
typedef T Temp; // note: non-const analog of Elem
typedef const T *Ptr;
};
It’s not the most readable implementation one can imagine, but it’s hid-
den, and our users can now invoke our generic algorithm with a container
generated from a standard vector.
std::vector<std::string> strings2;
aString = process( strings2, strings2.size() ); // works!
This page intentionally left blank
❘
Item 55 Template Template Parameters
For simplicity, let’s abandon the standard library (not usually a good idea,
by the way) and assume we have available a set of nonstandard container
templates: List, Vector, Deque, and perhaps others. Let’s also assume
these containers are similar to the standard containers but have only a
single template parameter for the element type of the container.
Recall that the standard containers actually have at least two parameters:
the element type and an allocator type. Containers use allocators to allocate
and free their working memory so that this behavior may be customized.
In effect, the allocator specifies a memory management policy for
the container (see Policies [56, 205]). The allocator has a default so it’s
easy to forget it’s there. However, when you instantiate a standard
container like vector<int>, you’re actually getting vector< int,
std::allocator<int> >.
Notice that we’ve left out the name of template parameter in the declara-
tion of List, above. Just as with a formal argument name in a function dec-
laration, giving a name to a template parameter in a template declaration is
optional. As with a function definition, the name of a template parameter
is required only in a template definition and only if the parameter name is
199
200 ❘ Item 55 Template Template Parameters
This helps in cases where the user of a Stack is willing to accept a Deque
implementation or doesn’t particularly care about the implementation.
Stack<int> aStack1; // container is Deque<int>
Stack<double> aStack2; // container is Deque<double>
Item 55 Template Template Parameters ❘ 201
This is more or less the approach employed by the standard container
adapters stack, queue, and priority_queue.
std::stack<int> stds; // container is
// deque< int, allocator<int> >
Let’s see if we can improve safety and still have reasonable flexibility. A tem-
plate can take a parameter that is itself the name of a template. These param-
eters have the pleasingly repetitious name of template template parameters.
template <typename T, template <typename> class Cont>
class Stack;
This new template parameter list for Stack looks unnerving, but it’s not
as bad as it appears. The first parameter, T, is old hat. It’s just the name of
a type. The second parameter, Cont, is a template template parameter. It’s
the name of a class template that has a single type name parameter. Note
that we didn’t give a name to the type name parameter of Cont, although
we could have:
template <typename T, template <typename ElementType> class Cont>
class Stack;
But compassion for the readers of our code does impose constraints on
such practices, even if the C++ language does not.
The Stack template uses its type name parameter to instantiate its tem-
plate template parameter. The resulting container type is used to imple-
ment the Stack:
template <typename T, template <typename> class Cont>
class Stack {
//...
private:
Cont<T> s_;
};
The Wrapper2 template needs a template name for its template argu-
ment, and not just any template name. The declaration says that the tem-
plate must take a single type argument.
Wrapper2<List> w4; // fine, List is a template one type
Wrapper2< List<int> > w5; // error! List<int> isn't a template
Wrapper2<std::list> w6; // error! std::list takes 2+ arguments
or equivalently:
template <template <typename,typename> class Cont>
class Wrapper3;
204 ❘ Item 55 Template Template Parameters
This declaration says that the template must take two type name arguments:
Wrapper3<std::list> w7; // might work...
Wrapper3< std::list<int> > w8; // error! list<int> is a class
Wrapper3<List> w9; // error! List takes one type argument
205
206 ❘ Item 56 Policies
C s_;
};
The destructor iterates over any remaining elements and executes the
appropriate deletion policy on each element. The doDeletionPolicy
could be implemented in a variety of ways. Typically, a policy is made
explicit when the Stack template is instantiated and is implemented with
a template template parameter (see Template Template Parameters [55, 199]).
template <typename T, template <typename> class DeletionPolicy>
class Stack {
public:
~Stack() {
for( I i( s_.begin() ); i != s_.end(); ++i )
DeletionPolicy<T>::doDelete( *i ); // exec policy
}
//...
private:
typedef std::deque<T> C;
typedef typename C::iterator I;
C s_;
};
If one policy is more commonly used than others, it’s often a good idea to
make it the default:
template <typename T,
template <typename> class DeletionPolicy = NoDeletePolicy>
class Stack;
//...
Stack<int> s6; // don't delete
208 ❘ Item 56 Policies
We may specialize the template explicitly when we call it, just as we must
specialize a class template:
int a = cast<int,double>(12.3);
However, it’s typical and more convenient to let the compiler deduce the
template arguments from the types of the actual arguments to the function
call. Not surprisingly, this process is called “template argument deduction.”
Careful! In the description below, pay attention to the difference between
the terms “template argument” and “function argument” (see Template
Terminology [45, 153]). Consider a template with a single template argu-
ment that finds the lesser of two function arguments.
template <typename T>
T min( const T &a, const T &b )
{ return a < b ? a : b; }
209
210 ❘ Item 57 Template Argument Deduction
The erroneous line above is the result of the compiler’s not being able to
deduce a template argument in an ambiguous situation. In such cases, we
can always tell the compiler what a template argument is by being explicit:
d = min<double>( 12.3, 4 ); // OK, T is double
A similar situation occurs with our cast template if we try to use tem-
plate argument deduction:
int a = cast( 12.3 ); // error! E is double, but what's R?
Notice that any trailing template arguments may be left off the template
argument list if the compiler can deduce them on its own. In this case we
had only to supply the compiler with the return type and let it figure out
the expression type on its own. The order of the template parameters is
important for the template’s usability, since if the expression type had
preceded the return type, we would have had to specify both explicitly.
At this point, many people will notice the syntax of the call to cast
above and ask, “Are you implying that static_cast, dynamic_cast,
const_cast, and reinterpret_cast are function templates?” No, we’re
not implying that because these four cast operators are not templates,
they’re built-in operators (like the new operator or the + operator on
integers); but it sure looks like their syntax was inspired by something
similar to our cast function template. (See New Cast Operators [9, 29].)
Item 57 Template Argument Deduction ❘ 211
Note that template argument deduction works by examining the types of
the actual arguments to a call. This implies that any template argument of
a function template that cannot be deduced from the argument types has
to be supplied explicitly. For example, here’s an annoyingly repetitious
function template:
template <int n, typename T>
void repeat( const T &msg ) {
for( int i = 0; i < n; ++i )
std::cout << msg << std::flush;
}
We were careful to put the integer template argument before the type
argument, so we could get by with specifying only the number of repeti-
tions of the message, and let template argument deduction determine the
type of the message:
repeat<12>( 42 ); // n is 12, T is int
repeat<MAXINT>( '\a' ); // n is big, T is char
In the cast, min, and repeat templates, the compiler deduced a single
template argument from a single function argument. However, the
deduction mechanism is capable of deducing multiple template argu-
ments from the type of a single function argument:
template <int bound, typename T>
void zeroOut( T (&ary)[bound] ) {
for( int i = 0; i < bound; ++i )
ary[i] = T();
}
//...
const int hrsinweek = 7*24;
float readings[hrsinweek];
zeroOut( readings ); // bound == 168, T is float
It’s common in cases like this to provide a “helper function” whose only
purpose is to deduce the template arguments in order to specialize,
automagically, a class template:
template <typename R, typename A1, typename A2>
inline PFun2<A1,A2,R> makePFun( R (*pf)(A1,A2) )
{ return PFun2<A1,A2,R>(pf); }
//...
std::sort(b, e, makePFun(isGreater)); // much better...
The first call with two double arguments could be made to match the
nontemplate g by converting the doubles to char implicitly (legal but
inadvisable), but an exact match is available by instantiating the template
g with T as double, so the template is chosen. The second call with double
and int arguments will not match the template g, because the compiler
will not attempt the predefined conversion from int to double on the
second argument (or from double to int on the first) so as to deduce T to
213
214 ❘ Item 58 Overloading Function Templates
217
218 ❘ Item 59 SFINAE
(sizeof(hasIterator<C>(0))==sizeof(True))
220 ❘ Item 59 SFINAE
This template can be used to sort an array of objects, provided that the
objects can be compared with a < operator and copied. For example, we
can sort an array of our String objects from Assignment and Initializa-
tion Are Different [12, 41]:
String names[] = { "my", "dog", "has", "fleece" };
const int namesLen = sizeof(names)/sizeof(names[0]);
slowSort( names, namesLen ); // sorts...eventually!
The first complaint one might make concerning slowSort is that it can
be slow. That observation is correct, but let’s forgive slowSort its O(n2)
runtime and concentrate instead on aspects of its generic design.
The first observation we can make is that our implementation of swap in
slowSort is not ideal for the String type (or many other types, for that
221
222 ❘ Item 60 Generic Algorithms
matter). The String class has its own member swap that is both faster
and more exception safe than a swap that is accomplished by copying
through a temporary String. A better implementation approach is simply
to say what we mean:
template <typename T>
void slowSort( T a[], int len ) {
for( int i = 0; i < len; ++i ) // For each pair
for( int j = i; j < len; ++j )
if( a[j] < a[i] ) // ...if out of order...
swap( a[j], a[i] ); // ...swap them.
}
We’re still not calling String’s swap member function, but if the author
of the String class has it together, there will be a nonmember swap
available that will:
inline void swap( String &a, String &b )
{ a.swap( b ); }
Our approach in STL Function Objects [20, 71] was to implement func-
tions and function objects that could be used in place of a < operator, but
this approach will work only if the generic algorithm has been designed
to accept such an argument:
template <typename T, typename Comp>
void slowSort( T a[], int len, Comp less ) {
for( int i = 0; i < len; ++i ) // For each pair
for( int j = i; j < len; ++j )
if( less( a[j], a[i] ) ) // ...if out of order...
swap( a[j], a[i] ); // ...swap them.
}
//...
State states[50];
//...
slowSort( states, 50, PopComp() );
Here we’ve replaced our clunky array interface with a more standard and
more flexible STL compliant iterator interface. Now we can feel comfort-
able calling slowSort a generic algorithm, rather than simply a function
template.
One important lesson of this example is that complex software design is
nearly always a group effort. As such, your code should be designed in
such a way as to leverage the expertise of your colleagues while remaining
as immune as possible to maintenance they perform on code that is not in
your control. Our improved slowSort algorithm is a good example of
such proper design. It performs a single, well-understood operation at as
high a conceptual level as possible. To be precise, slowSort handles the
sorting algorithm and subcontracts swapping and comparison to others
who will do a better job. This approach allows you, the (supposed) sort-
ing expert, to augment your sorting expertise with the swapping expertise
of whoever designed the element type that is being sorted. The two of you
may never meet, but through proper design you can work together as
closely as if you shared the same workstation. Moreover, if improved swap
functionality should appear in the future, slowSort will pick up the
improvement automatically and probably without your knowledge. As
ever, ignorance is strength. (This is similar in flavor to proper polymorphic
design; see Commands and Hollywood [19, 67].)
❘
Item 61 You Instantiate What You Use
In both C and C++, if you don’t call a declared function (or take its
address), you don’t have to define it. An analogous situation occurs with
member functions of class templates; if you don’t actually call a template’s
member function, it’s not instantiated.
This is clearly a handy property for the purpose of reducing code size. If a
class template defines a large number of member functions, but you use
only two or three of them, you don’t pay the code space penalty for all
those unused functions.
An even more important result of this rule is that you can specialize class
templates with arguments that would be illegal if all the member functions
were instantiated. With this rule in place, it’s possible to write flexible
class templates that can work with a wide variety of arguments, even if
some arguments would result in erroneous instantiations of some member
functions; if you don’t actually call those erroneous functions, they’re not
instantiated, and you don’t get an error. This is consistent with many
areas of the C++ language, where potential problems are not flagged as
errors until they become actual problems. In C++, it’s OK to think illegal
thoughts as long as you don’t act on them!
Consider a simple, fixed-size array template:
template <typename T, int n>
class Array {
public:
Array() : a_( new T[n] ) {}
~Array() { delete [] a_; }
Array( const Array & );
Array &operator =( const Array & );
void swap( Array &that ) { std::swap( a_, that.a_ ); }
T &operator []( int i ) { return a_[i]; }
const T &operator []( int i ) const { return a_[i]; }
225
226 ❘ Item 61 You Instantiate What You Use
This container behaves pretty much like a predefined array, with the usual
operations for indexing, but it also provides some higher-level operations
that are not available on predefined arrays, like swapping and comparison
for equality (we’ve left out the relational operators for reasons of space).
Let’s look at an implementation of operator ==:
template <typename T, int n>
bool Array<T,n>::operator ==( const Array &that ) const {
for( int i = 0; i < n; ++i )
if( !(a_[i] == that.a_[i]) )
return false;
return true;
}
We know that both arrays being compared have the same number of
elements, since they’re both the same type and the array size is one of the
template parameters, so we just have to perform a pairwise comparison
of each element. If any pair of elements differs, the Array objects are
not equal.
Array<int,12> a, b;
//...
if( a == b ) // calls a.operator ==(b)
//...
Production C++ applications tend to use a lot of header files, and many
header files include other header files. Under these circumstances, it’s
common for the same header file to be indirectly included more than
once in a compilation, and it’s not uncommon in large, complex applica-
tions for the same header file to occur hundreds of times in the same
compilation. Consider the simple case of a header file hdr2.h that
includes another header file, hdr1.h, and a header file hdr3.h that also
includes hdr1.h. If both hdr2.h and hdr3.h are included in the same
source file, hdr1.h will be included twice. Typically, such multiple inclu-
sions are undesirable and cause multiple definition errors.
For this reason, C++ header files almost universally employ a preprocessor
coding technique to prevent the content of the header from appearing
more than once in a compilation no matter how many times the header
file is actually #included. Consider the content of header file hdr1.h:
#ifndef HDR1_H
#define HDR1_H
// actual content of the header file...
#endif
The first time the header file hdr1.h is #included in a compilation, the
preprocessor symbol HDR1_H is undefined, so the #ifndef (“if not
defined”) preprocessor conditional allows preprocessing of the #define
directive and the rest of the header file’s content. The next time hdr1.h
appears in the same compilation, the symbol HDR1_H is defined, and the
#ifndef prevents repeated inclusion of the header file’s content.
This technique will work only if the preprocessor symbol for a header file
(in this case, HDR1_H) is associated with exactly one header file (in this
case, hdr1.h). It’s therefore important to establish a standard, simple
naming convention that allows the construction of the name of the pre-
processor symbol used in the include guard from the name of the header
file being guarded.
229
230 ❘ Item 62 Include Guards
Rather than simply #include a header file, we guard the inclusion with a
test on the same guard symbol that is set within the header file. This is
redundant, because the first time a header file is included, the same con-
dition (in this case, #ifndef HDR1_H) will be tested twice, both before the
#include and within the header file itself. However, on subsequent inclu-
sions, the redundant guard will prevent the #include directive from
being executed, preventing the header file from being needlessly opened
and scanned. Use of redundant include guards is not as common as that
of simple include guards, but in some cases their use can improve compi-
lation times of large applications considerably.
❘
Item 63 Optional Keywords
Some keyword usage is strictly optional from the perspective of the C++
language, though other considerations may argue for their presence or
absence.
The most common source of confusion is the optional use of virtual in
a derived class member function that overrides a base class virtual mem-
ber function.
class Shape {
public:
virtual void draw() const = 0;
virtual void rotate( double degrees ) = 0;
virtual void invert() = 0;
//...
};
class Blob : public Shape {
public:
virtual void draw() const;
void rotate( double );
void invert() = 0;
//...
};
The member function Blob::draw overrides the base class draw function
and so is virtual; the use of the keyword is completely optional and has no
effect on the meaning of the program. A common misassumption is that
omitting the virtual keyword will prevent further overriding in more
derived classes. This is not the case.
class SharpBlob : public Blob {
public:
void rotate( double ); // overrides Blob::rotate
//...
};
231
232 ❘ Item 63 Optional Keywords
Some authorities claim that it’s best to be specific and always declare these
functions to be explicitly static. Others think that if a user or maintainer
of a piece of C++ does not know these functions are implicitly static, they
should not be using or maintaining the code. The use of static here is a
waste of effort; a program is no place to put crib notes on language
semantics. As with the optional use of virtual, whatever your position
on optional static, it’s important to be consistent. Either all four of
these functions should be declared explicitly static or none of them
should.
In a template header, the keywords typename and class may be used
interchangeably to indicate that a template parameter is a type name;
there is no difference in meaning whatsoever. However, many expert C++
programmers use typename to indicate to the human reader that the
Item 63 Optional Keywords ❘ 233
template argument may be of any type and class to indicate that the
type argument must be a class type.
template <typename In, typename Out>
Out copy( In begin, In end, Out result );
235
This page intentionally left blank
Index
See also Index of Code Examples, page 245
->* (dash angle bracket Allocators, rebind convention, and virtual function table
asterisk), pointer-to- 180 pointers, 38
member operator, 58 Anonymous namespaces, 84–85 Asterisk (*) operator,
-> operator, overloading, Argument dependent lookup overloading, 145–146,
145–146, 147–148 (ADL), 89–90 147–148
( ) (parentheses) Arguments, templates, 153–154 Audit trails, resource control.
function call operator, 58 Array declarators, pointers to, See RAII.
grouping in declarations, 61 61–62 auto keyword, 233
grouping with pointer-to- Array formal arguments, 17–19 auto_ptr
member operators, 58 Array index calculation. See array references, 148
* (asterisk) operator, overload- pointer arithmetic. as container elements, 148
ing, 145–146, 147–148 Arrays conversions, 147–148
.* (dot asterisk), pointer-to- allocation/deallocation, description, 147–148
member operator, 58 127–129 operator overloading, 147
1984 (veiled reference), 224 auto_ptr references, 148 versus smart pointers, 148
of class objects, 120–121
decay, 17
A as function arguments. See B
ABC (abstract base class), 113 array formal arguments. Base classes
abort, 117, 140 memory management, abstract bases,
Abstract data type. See data 127–129 manufacturing, 113–115
abstraction. of pointers, 25 forcing to abstract base,
Access protection, 88, 111, references to, 62 113–115
113–115 sorting, 221–222 implementation
Accessor functions. See get/set Assignment. See also copying; guidelines, 77–79
interfaces. initialization. member function,
Address arithmetic. See pointer computational constructor, determining, 77–79
arithmetic. 43 polymorphic, 3–5
ADL (argument dependent construction, 42 Template Method, 77–79
lookup), 89–90 copying, 45–47 Body objects, 117
Alexandresu, Andrei, xviii destruction, 42–43 Boedigheimer, Kim, xvii
Aliases, 84. See also references. exception safe copy, 46 Bradbury, Ray (veiled
Allison, Chuck, xvii versus initialization, 41–43 reference), 50
Allocating arrays, 127–129 user-defined types, 41–43 The Brother, xv, 81, 147
virtual copy, 47
237
238 ❘ Index
C virtual inheritance, 37–38 copying, 45–47
Callbacks. See also Command what you see is Construction order, 141–142
pattern. what you get, 37 Constructors
definition, 67 Class members, pointers calling, 119–121
“don’t call us, to versus pointers, 53–56 description, 143–144
we’ll call you,” 68 Class objects. See objects. exception safety, 143–144
framework code, 68 Classes operator overloading, 143–144
function objects as, 67–70. handle, exception placement new, 119–121
See also Command pattern. safe swaps, 46 protected, 114–115
function pointers, 50–51 interface, 93–94 virtual, 99–101
Hollywood Principle, 68 Cloning objects, 99–101. See Container elements,
Capability queries, 93–95 also assignment; copying; auto_ptr as, 148
Cast operators initialization; Prototype. Contract, base class as, 4–5
casting down Command pattern, 69. See also Contravariance
an inheritance callbacks. class layout, 54–55
hierarchy, 30–31 Communication, with other member templates, 174
from a pointer to a base programmers pointers to data
class, 31 data abstraction, 2 members, 54–55
to a reference type, 32 design patterns, 7–8 pointers to member
const qualifiers, identifiers in template functions, 54–55, 58
adding/removing, 29–30 declarations, 201–202 Conventions. See also idioms.
const_cast, 29–30 versus ignorance, 224 anonymous temporary
conversion across overloading, 214–215 function object, 73
a hierarchy, 94–95 typedefs, 62 class vs. typename, 232–233
cross-cast, 94–95 Comparator operations, 71 copy operations, 45
dynamic_cast, 31–32 Comparators, STL function exception safety axioms,
function style, 29 objects as, 72–73 131–133
new style Complete specialization. See generic programming, 191,
description, 29–32 explicit specialization. 193, 207, 223
versus old-style, 29 Computational in generic programming, 170
old-style versus new style, 29 constructor, 41, 43 multilevel pointers, 26
reinterpret_cast, 31 Const member functions naming conventions, 88, 229
static_cast, 30–32 lazy evaluation, 34 placement of const
type qualifiers, changing, logically const, 35 qualifier, 21–22
29–30, 30–31 meaning of, 33–36 rebind convention for
volatile qualifiers, modifying objects, 34 allocators, 180
adding/removing, 29–30 overloaded index and the STL, 12
Class layout operator, 35–36 STL (standard template
assignment, and virtual Const pointers, versus pointers library), 11
function table pointers, 38 to const, 21–23 traits and promulgation, 193,
contravariance, 54–55 const qualifiers, 197
covariant return types, 109 adding/removing, 29–30 unnecessary static, 232
member offsets, 39 const_cast operator, 29–30 unnecessary virtual, 232
pointer comparison, 97–98 Construction Conversions, auto_ptr,
virtual functions, 37 assignment, 42 147–148
Index ❘ 239
Copying. See also assignment; Disambiguation volatile qualifiers,
cloning; initialization. auto keyword, 233 adding/removing, 29–30
address of non-const to register keyword, 233
pointer to const, 22 with template, 179–181
assignment, 45–47 templates, 179–181 F
class objects, 38 with typename, 169–172 Fahrenheit 451 (veiled
construction, 45–47 “Don’t call us, we’ll call reference), 50
objects, 38 you,” 68, 79 Fehér, Attila, xvii
Covariance, 174 Dot asterisk (.*), pointer-to- File handles, resource control.
Cranberries, xviii, 183 member operator, 58 See RAII.
Cross-cast, 94–95 Down casting. See also cast Fire safety advice, 50
operators. Framework code, callbacks, 68
runtime type information, 31 Function declarators, pointers
D safe, 31 to, 61–62
Dash angle bracket asterisk dynamic_cast operator, 31–32 Function objects. See also
(->*), pointer-to-member function pointers;
operator, 58 smart pointers.
Data abstraction, 1–2 E as callbacks, 67–70. See also
Data hiding. See access protec- e, 65, 66 Command pattern.
tion; data abstraction. Element type, determining, description, 63–66
Deallocating arrays, 127–129 189–191 integrating with member
Decay Embedded type information, functions, 66
arrays, 17, 25 189–191 Function overloading. See also
functions, 17, 72 Exception safety operator overloading.
Declaring function pointers, 49 axioms, 131–133 function templates, 213
Delta adjustment of class catching, 135–137 generic algorithms, 223
pointer, 58, 98, 108 constructors, 143–144 infix calls, 90
Design patterns copy assignment, 46 overloaded index operator,
description, 7–10 exceptions, 143–144 35–36
essential parts, 8–10 functions, 135–137 pointers to overloaded
Factory Method, 103–106, new operator, 143–144 functions, 51
108–109 safe destruction, 132 required knowledge, xiii
microarchitectures, 10 swap throwing, 133 scope, 88
Template Method synchronous exceptions, SFINAE, 217
versus C++ templates, 77 131–132 taking address of, 51
description, 77–79 throwing, 135–137 Function pointers. See also
wrappers, 8 Exceptions, memory allocation, function objects.
Destruction 143–144 callbacks, 50–51
assignment, 42–43 exit and destruction, 117, 140 declaring, 49
order, 141–142 Explicit specialization, 155–159, description, 49–51
RAII, 140 183–187 generic, 49–50
restricting heap allocation, Expressions to inline functions, 50
117 const qualifiers, to non-static member
Dewhurst, David R., xviii adding/removing, 29–30 functions, 50
240 ❘ Index
Function pointers, continued restricting heap allocation, RAII, 139–142
to overloaded functions, 51 117 reference to pointer formal
virtual, 64–66 Header files, multiple inclusion, argument, 26
Function style, 29 229–230 resource acquisition is
Function templates Heap initialization, 139–142
generic algorithms, 221–224 algorithms, 155 to restrict heap allocation,
versus nontemplate functions, allocation, restricting, 117–118
213 117–118 result of assignment vs.
overloading, 213–215 class template explicit initialization, 46
template argument specialization, 155–159 robust computation of
deduction, 209–212 Heinlein, Robert (veiled array size, 17
Functionoid. See function reference), 76 smart pointer, 145–147
objects. Henney, Kevlin, xvii STL function object, 72–73
Functions Hewins, Sarah G., xviii violation of copy operation
arguments from arrays. See Hitchhiker’s Guide to the idioms, 148
array formal arguments. Galaxy, (veiled reference), virtual constructor, 100
decay, 17, 72 211, 213 Ids, templates, 154
multilevel pointers, 25, 27 Hollywood principle, 68, 79, Ignorance
references to, 62 224 callbacks, 67
selecting right version, 214 healthful aspects, 5, 12, 224
static linkages, avoiding, 85 Java programmers, 37
Functor. See function objects. I of object type, 100–101
Idioms pitfalls of, xii
assumption of non-throwing and templates, 180
G deletion, 136 Initialization. See also
Generic function pointers, checking for assignment to assignment; copying.
49–50 self, 47 argument passing, 41
Gentleman Cambrioleur, 196 computational constructor, versus assignment, 41–43
get/set interfaces, 1 43 catching exceptions, 41
Global scope, namespaces, to create abstract base class, declaration, 41
81–85 113–115 function return, 41
Goldfedder, Brandon, xvii exception safety axioms, Initialization order. See
Gordon, Peter, xvii 131–133 construction order.
Graphical shapes, resource function object, 63 Inline functions, pointers to, 50
control. See RAII. getting current new_handler, Instantiation
Guarding against multiple 51 template member functions,
inclusions, 229–230 intent of dynamic_cast to ref- 225–227
erence, 32 templates, 154
meaning of assignment, 46 Integers as pointers
H ordering code for exception new cast operators, 29, 31
Handle/body idiom, 117 safety, 136 placement new, 119
Handle classes partitioning code to avoid pointer arithmetic, 151
exception safe swaps, 46 RTTI, 94 Interface class, 65, 93
RAII, 139 to prohibit copying, 111
Index ❘ 241
J integrating with function Nested names, templates, 179
Java objects, 66 Network connections, resource
versus C++, xii operator precedence, 58, 61 control. See RAII.
function and array versus pointers, 57–59 New cast operations. See cast
declarators, 62 simple pointer to function, operators, new style.
good natured poke at, 37 58 new operator
interface classes, 93 virtualness, 58 description, 143–144
member function lookup, 88 roles, 77–78 exception safety, 143–144
Johnson, Matthew, xvii templates, 173–177 versus operator new, 119,
Josuttis, Nicolai, 217–220 Member object. See class layout. 123, 143
Member offset. See class layout. operator overloading,
Member operator functions, 143–144
K overloading non-member New style cast operators, versus
Kamel, Moataz, xvii operators, 91–92 old-style, 29
Keywords, optional, 231–233 Member specialization, 165 1984 (veiled reference), 224
Koenig lookup. See ADL (argu- Member templates, 173–177 Non-static member functions,
ment dependent lookup). Memory pointers to, 50
Kyu Sakamoto, reference to, 42, class-specific management, Nontemplate functions versus
68, 70 123–126 function templates, 213
heap allocation, restricting,
117–118
L resource control. See RAII. O
Lazy evaluation, 34 Memory management, arrays, Objects
Lippman, Stan, 141 127–129 alternate names for. See
Logically const, 35 Meyers, Scott, xvii, 1 aliases; references.
Login sessions, resource control. Multidimensional arrays arrays of, 120–121
See RAII. array formal arguments, capability queries, 93–95
Lupin, Arsene, 196 18–19 changing logical state, 33–36
function and array cloning, 99–101
declarators, 61 copying, 38
M pointer arithmetic, 150 copying, prohibiting, 111
Managers, gratuitous swipe at, creating, based on existing
10 objects, 103–106
Martin, Steve (veiled reference), N heap allocation, restricting,
46 Names, templates, 154 117–118
McFerrin, Bobby (veiled Namespaces integrating member
reference), 179 aliases, 84 functions with function
Member functions anonymous, 84–85 objects, 66
function matching errors, continual explicit lazy evaluation of values, 34
87–88 qualification, 82–83 managing with RAII,
integrating function objects, description, 81–85 139–142
66 names modifying, 34
lookup, 87–88 declaring, 82 with multiple addresses. See
pointers to importing, 83 pointer comparison.
contravariance, 54–55 using declarations, 84 with multiple types. See
declaration syntax, 57–58 using directives, 82–83 polymorphism.
242 ❘ Index
Objects, continued Operator precedence, pointers Pointers. See also smart
polymorphic, 3–5 to member functions, 58, pointers.
structure and layout, 37–39 61 arrays of, 25
traits, 193–197 Orwell, George (veiled dereferencing, 53–54
type constraints, 111 reference), 224 integers as, 151
virtual constructors, 99–101 Ottosen, Thorsten, xvii list iterators, 151
Offset. See delta adjustment of Overloading losing type information, 98
class pointer; pointers to, -> operator, 145–146, managing buffers of, 25
data members. 147–148 multilevel. See pointers to,
Old-style cast operators, versus * (asterisk) operator, pointers.
new style, 29 145–146, 147–148 versus references, 13
operator delete as communication, with stacks of, 185–187
class-specific memory other programmers, subtracting, 151
management, 123–126 214–215 Pointers to
usual version, 125 function call operators, 63 array declarators, 61–62
Operator function lookup, function templates, 213–215, characters, 156
91–92 217–220 class members, versus
operator new functions. See function pointers, 53–56
class-specific memory overloading. const
management, 123–126 index operator, 35–36 versus const pointers,
versus new operator, 119, 123, operators. See operator 21–23
143 overloading. converting to pointer to
placement new, 119–121 operators, policies, 206 non-const, 23
usual version, 125 versus overriding, 75–76 data members, 54–55
Operator overloading. See also Overriding function declarators, 61–62
function overloading. functions, covariant return functions. See callbacks;
auto_ptr, 147 types, 107–109 Command pattern;
constructors, 143–144 versus overloading, 75–76 function pointers.
exception safety, 131 member functions
exceptions, 143–144 contravariance, 54–55
function calls, 91–92 declaration syntax, 57–58
function objects, 63 P integrating with function
infix calls, 90–92 Parameters, templates, 153–154 objects, 66
new operator, 143–144 Parentheses (( )) operator precedence, 58, 61
operator function lookup, function call operator, 58 versus pointers, 57–59
91–92 grouping in declarations, 61 simple pointer to function,
versus overriding, 75 grouping with pointer-to- 58
placement new, 119 member operators, 58 virtualness, 58
pointer arithmetic, 149–151 Partial specialization, 161, non-const, converting to
policies, 206 183–187, 197 pointer to const, 22–23
smart pointers, 145–146 Patterns. See design patterns. pointers
STL function objects, 72 Paul Revere and the Raiders, changing pointer values, 26
STL (standard template 143 conversions, 26–27
library), 11 Pointer arithmetic, 19, 149–151 converting to pointer to
Pointer comparison, 97–98 const, 27
Index ❘ 243
converting to pointer to initialization, 14–15 Specializing for type
non-const, 27 to non-const, 15 information, 183
description, 25–27 null, 13–14 Standard template library
managing buffers of versus pointers, 13 (STL), 11–12. See also pri-
pointers, 25–26 register keyword, 233 mary templates; templates.
void, 98 reinterpret_cast operator, Static linkages, avoiding, 85
Policies, 205–208 31 static_cast operator, 30–32
Polymorphic base classes, 3–5 Resource acquisition is STL function objects
Polymorphism, 3–5 initialization. See RAII. as comparators, 72–73
Predicates, STL function objects Resource control. See RAII. description, 71–74
as, 73–74 RTTI (runtime type as predicates, 73–74
Primary templates. See also STL information) true/false questions, 73–74
(standard template for capability query, 95 STL (standard template
library); templates. incorrect use of, 103 library), 11–12. See also
explicit specialization, runtime cost of, 31 primary templates;
155–158, 183–187 for safe down cast, 31 templates.
instantiation, 154 Subcontractor, derived class as,
member specialization, 165 4–5
partial specialization, 161, S Substitution failure is not an
183–187 Sakamoto, Kyu (veiled error (SFINAE), 217–220
SFINAE, 218 reference), 42, 68, 70 Sutter, Herb, xi, xvii, xviii, 136
specialization, 154 Saks, Dan, xvii Swedish, and technical
specializing for type Semaphores, resource control. communication, 99
information, 183 See RAII.
Promulgation, conventions, SFINAE (substitution failure is
193, 197 not an error), 217–220 T
Prototype, 99–101 Single-dimensional arrays, array Template argument deduction,
formal arguments, 17–18 18, 209–212
Slettebø, Terje, xvii Template Method
Q Smart pointers. See also func- versus C++ templates, 77
QWAN (Quality Without A tion objects; pointers. description, 77–79
Name), 90 versus auto_ptr, 148 Template template parameters,
list iterators, 11, 151 199–204
operator overloading, Templates. See also primary
R 145–146 templates; STL (standard
RAII, 139–142. See also templates, 145–146 template library).
auto_ptr. Social commentary, xiii, 7, 10, arguments
Rebind convention for 71, 190, 195 customizing. See templates,
allocators, 180 Specialization specialization.
Reeves, Jack, xviii explicit, 183–187 description, 153–154
References. See also aliases. partial, 183–187 array formal arguments,
to arrays, 62 SFINAE, 218 18–19
to const, 15 templates. See templates, C++ versus Template
description, 13–15 specialization. Method, 77
to functions, 62 for type information, 183
244 ❘ Index
Templates, continued pointers to member functions Using declarations, 84
customizing. See templates, versus pointers, 57–59 Using directives, 82–83
specialization. references versus pointers, 13 Usual operator new and
disambiguation, 169–172, template argument operator delete, 125
179–181 deduction, 209
ids, 154 Template Method versus C++
ignorance and, 180 templates, 77 V
instantiation, 154 templates, 153–154 Vandevoorde, David, 217–220
member functions, 173–177 wrappers, 8 Variables, avoiding static
names, 154 Tondo, Clovis, xvii linkage, 85
nested names, 179 Traits Virtual constructors, 99–101
parameters, 153–154 conventions, 193, 197 Virtual copy, assignment, 47
smart pointers, 145–146 description, 193–197 Virtual function pointers, 64–66
specialization specialization, 197 Virtual functions, class layout,
explicit, 155–159, 183–187, templates, 195–197 37
209–212 Traits class, 193–197 Virtual inheritance, class layout,
partial, 161–164, 183–187 True/false questions, 73–74 37–38
terminology, 153–154 Type virtual keyword, 231–233
traits, 197 container elements, volatile qualifiers,
traits, 195–197 determining, 189–191 adding/removing, 29–30
Terminology information, embedded,
assignment versus 189–191
initialization, 41–43 information about, 193–197 W
const pointers versus pointers qualifiers, changing, 29–30, Ward, Dick and Judy, xviii
to const, 21–23 30–31 What you see is what you get, 37
member templates, 174 traits, 193–197 Wilson, Flip (veiled reference),
new operator versus opera- Typename, disambiguation, 37
tor new, 119, 123, 143 169–172 Wilson, Matthew, xvii
new style cast operators
versus old-style, 29
overloading versus U Z
overriding, 75–76 Unnecessary static conventions, Zolman, Leor, xvii
pointers to class members 232 Zoo animals, resource control.
versus pointers, 53–56 Unnecessary virtual See RAII.
pointers to const versus const conventions, 232
pointers, 21–23 User-defined types, assignment,
41–43
Index of Code Examples
See also Index, page 237
245
246 ❘ Index
Class templates, continued copy operations, 45 Command pattern, 69
PtrDeletePolicy, 206–207 optional keywords, 232 Comparator
PtrList, 169 restricting heap allocation, PopLess class, 72
PtrVector, 25 118 popLess function, 71
ReadonlySeq, 191 Heap<char *>, 157–158 PtrCmp class template, 162
SCollection, 170 Heap<const char *>, 156 strLess function, 157
Seq, 189–190 IsWarm, 73 Computational constructor, 43
SList, 173, 180 Meal, 100 ContainerTraits class
Stack, 185–186, 200–202, MyApp, 78–79 template, 194
205–206 MyContainer, 170 ContainerTraits
Wrapper1, 203 MyHandle, 124 < vector<T> > class
Wrapper2, 203 NMFunc, 66 template, 197
Wrapper3, 203–204 NoCopy, 111 ContainerTraits<const
Classes NoHeap, 117–118 char *> class, 196
ABC, 113–114 ObservedBlob, 97 ContainerTraits<const T
Action, 69 OnHeap, 118 *> class template, 197
App, 78 PlayMusic, 69 ContainerTraits
B, 59, 75, 87 PopLess, 72 <ForeignContainer>
Blob, 231 rep, 125 class, 195
Button, 67, 69 ResourceHandle, 139 ContainerTraits<T *> class
C, 54 Rollable, 93 template, 196
Circle S, 38 Copy assignment
capability queries, 94 Shape Handle::operator =
covariant return types, capability queries, 93 member function, 46–47
107–108 covariant return types, Handle::swap member
pointers to class members, 107–108 function, 46
55 optional kwds, 231 Covariant return, 107–108
pointers to member pointer comparison, 97
functions, 57 pointers to class members,
CircleEditor, 108 55 D
ContainerTraits<const pointers to member D class, 59, 76, 87
char *>, 196 functions, 57
ContainerTraits ShapeEditor, 108
<ForeignContainer>, SharpBlob, 231 E
195 Spaghetti, 100 E class, 88
D, 59, 76, 87 Square, 94 Employee class, 104–105
E, 88 State, 71, 222–223 Exceptions
Employee, 104–105 String, 41 aTemplateContext
Fib, 63 Subject, 97 function template, 132
ForeignContainer, 195 T, 39 Button::setAction
Func, 65 Temp, 105 member function, 136
Handle Trace, 141 f function, 140–141
array allocation, 127–128 Wheel, 94 ResourceHandle class, 139
class-specific memory X, 33–36, 91 String::operator =
management, 123–124 cleanupBuf function, 121 member function, 135
Index ❘ 247
Trace class, 141 PFun2 class template, 212, G
X::X member function, 133 214 g function, 213
Explicit instantiation PlayMusic class, 69 g function template, 213
Array<Circle,7>, 227 PopLess class, 72 Generic algorithms, 221–224
Heap<double>, 167 PtrCmp class template, 162 genInfo function, 104
Explicit specialization Function template overloading
ContainerTraits<const g function, 213
char *> class, 196 g function template, 213 H
ContainerTraits makePFun function template, Handle class
<ForeignContainer> 215 array allocation, 127–128
class, 195 Function templates class-specific memory
Heap class template, 155 aTemplateContext, 132 management, 123–124
Heap<char *> class, cast, 209 copy operations, 45
157–158 extractHeap, 158 optional keywords, 232
Heap<const char *> class, fill, 170, 172 restricting heap allocation,
156 g, 213 118
IsInt class template, 183 makePFun, 212, 214–215 Handle::operator =
extractHeap function min, 209 member function, 46–47
template, 158 process, 19, 189, 191, 194 Handle::operator delete
process_2d, 19 member function, 126
repeat, 211 Handle::operator new
F set_2d, 15 member function, 125
f function, 140–141 slowSort, 221–224 Handle::swap member
Factory Method swap, 14, 45 function, 46
Circle class, 108 zeroOut, 211 hasIterator preprocessor
Employee class, 104–105 Functions macro, 219
Shape class, 108 aFunc, 82–84, 89, 118 Heap class template, 155, 165
Temp class, 105 append, 120–121 Heap<char *> class, 157–158
Fib class, 63 begForgiveness, 51 Heap<const char *> class,
fibonacci function, 64 cleanupBuf, 121 156
Fib::operator () member f, 140–141 Heap<const char *>::pop
function, 63 fibonacci, 64 member function, 166
fill function template, 170, 172 g, 213 Heap<const char *>::push
ForeignContainer class, 195 genInfo, 104 member function, 157,
friend function, 43 integrate, 65 166–167
Func class, 65 operator new, 119 Heap<double> explicit
Function object org_semantics:: instantiation, 167
Action class, 69 operator +, 82 Heap<T *> class template, 161
Fib class, 63 popLess, 71 Heap<T *>::push template
Func class, 65 scanTo, 26 member function, 162
IsWarm class, 73 someFunc, 115 Heap<T>::pop template
MFunc class template, 66 String::operator +, 43 member function, 156
NMFunc class, 66 strLess, 157 Heap<T>::push template
PFun1 class template, 215 swap, 222 member function, 155
Helper function, 212, 214
248 ❘ Index
I Member new operator new function, 119
integrate function, 65 Handle class, 123–124 org_semantics namespace,
Interface class Handle::operator new 81, 89
Action class, 69 member function, 125 org_semantics::operator
Func class, 65 MyHandle class, 124 + function, 82
Rollable class, 93 Member specialization
IsArray class template, 187 Heap<const char
IsClass class template, 219 *>::pop member P
IsInt class template, 183 function, 166 Partial specialization
IsPCM class template, 187 Heap<const char ContainerTraits
IsPtr class template, 184 *>::push member < vector<T> > class
is_ptr preprocessor macro, function, 157, 166–167 template, 197
217 Member templates ContainerTraits<const
IsWarm class, 73 AnAlloc::rebind, 179 T *> class template, 197
SList<T>::operator =, ContainerTraits<T *>
175 class template, 196
M SList<T>::SList, 174–175 Heap<T *> class template,
makePFun function template, SList<T>::sort, 176 161
212, 214–215 MFunc class template, 66 Heap<T *>::push template
Meal class, 100 minimum function template, member function, 162
Member array new, 118, 128 209 IsArray class template, 187
Member delete, 126 Multiple inheritance, 97 IsPCM class template, 187
Member functions MyApp class, 78–79 IsPtr class template, 184
App::startup, 78 MyContainer class, 170 PFun1 class template, 215
Button::setAction, 136 MyHandle class, 124 PFun2 class template, 212, 214
Fib::operator (), 63 Placement new
Handle::operator =, append function, 120–121
46–47 N operator new function, 119
Handle::operator Namespaces PlayMusic class, 69
delete, 126 aFunc function, 82–84 POD, 38
Handle::operator new, org_semantics, 81, 89 Pointer arithmetic
125 org_semantics namespace, process_2d function
Handle::swap, 46 81 template, 19
Heap<const char org_semantics:: set_2d function template, 15
*>::pop, 166 operator + function, 82 Policy
Heap<const char NMFunc class, 66 ArrayDeletePolicy class
*>::push, 157, 166–167 NoCopy class, 111 template, 207
String::operator =, 42, NoDeletePolicy class NoDeletePolicy class
135 template, 207 template, 207
String::String, 42 NoHeap class, 117–118 PtrDeletePolicy class
String::String, 42–43 template, 206–207
X::getValue, 34–35 Stack class template,
X::memFunc2, 91 O 205–206
X::X, 133 ObservedBlob class, 97 PopLess class, 72
OnHeap class, 118 popLess function, 71
Index ❘ 249
Predicate, 73 Shape class T
Preprocessor macro capability queries, 93 T class, 39
hasIterator, 219 covariant return types, Temp class, 105
is_ptr, 217 107–108 Template argument deduction
process function template, 19, optional kwds, 231 cast function template, 209
189, 191, 194 pointer comparison, 97 makePFun function template,
process_2d function pointers to class members, 55 212, 214
template, 19 pointers to member minimum function template,
Prototype functions, 57 209
Action class, 69 ShapeEditor class, 108 repeat function template,
Circle class, 107 SharpBlob class, 231 211
Meal class, 100 SList class template, 173, 180 zeroOut function template,
PlayMusic class, 69 SList<T>::empty template 211
Shape class, 107 member function, 173 Template member class, 174
Spaghetti class, 100 SList<T>::Node template Template member functions
PtrCmp class template, 162 member class, 174 Array<T,n>::operator
PtrDeletePolicy class SList<T>::operator = ==, 226
template, 206–207 member template, 175 Heap<T *>::push, 162
PtrList class template, 169 SList<T>::SList member Heap<T>::pop, 156
PtrVector class template, 25 template, 174–175 Heap<T>::push, 155
SList<T>::sort member SList<T>::empty, 173
template, 176 Stack<T>::push, 185
R slowSort function template, Template Method
ReadonlySeq class template, 221–224 App class, 78
191 Smart pointer, 145 App::startup member
rep class, 125 someFunc function, 115 function, 78
repeat function template, 211 Spaghetti class, 100 MyApp class, 78–79
ResourceHandle class, 139 Square class, 94 Trace class, 141
Rollable class, 93 Stack class template, 185–186, Traits
200–202, 205–206 ContainerTraits class
Stack<T>::push template template, 194
S member function, 185 ContainerTraits
S class, 38 State class, 71, 222–223 < vector<T> > class
scanTo function, 26 String class, 41 template, 197
SCollection class template, String::operator + ContainerTraits<const
170 function, 43 char *> class, 196
Seq class template, 189–190 String::operator = ContainerTraits<const
set_2d function template, 15 member function, 42, 135 T *> class template, 197
SFINAE String::String member ContainerTraits
CanConvert class template, function, 42 <ForeignContainer>
220 String::String member class, 195
hasIterator preprocessor function, 42–43 ContainerTraits<T *>
macro, 219 strLess function, 157 class template, 196
IsClass class template, 219 Subject class, 97
is_ptr preprocessor macro, swap function, 222
217 swap function template, 14, 45
250 ❘ Index
W X Z
Wheel class, 94 X class, 33–36, 91 zeroOut function template,
Wrapper1 class template, 203 X::getValue member func- 211
Wrapper2 class template, 203 tion, 34–35
Wrapper3 class template, X::memFunc2 member
203–204 function, 91
X::X member function, 133
C++ Courses and Services
www.semantics.org
If you liked any of these books, consider on-site and public training offered
by the author. His popular courses include:
Visit the Semantics web site for more information. You’ll also find Steve’s published
articles, “Once, Weakly” Web articles, research on C++ programming techniques,
and speaking schedule.
New and Classic C++ Books
This classic, tutorial introduction to standard C++ Whether you're working alone or with others, C++
has been completely updated, reorganized, and Coding Standards will help you write cleaner code—
rewritten to help programmers learn the language and write it faster, with fewer hassles and less
faster and use it in a more modern style. frustration.
ISBN 0201721481 ISBN 0321113586
912 pages • © 2005 240 pages • © 2005
Look
for new
edition in
2005
Visit us online for more books and more information, and to read sample chapters:
www.awprofessional.com
#33: Manufacturing Abstract #49: Disambiguating with
Bases (113) Typename (169)