Two things:
- Effective C++ Errata Updated
- My November 2002 CUJ Article is now Online
I just updated the errata list for Effective C++. As always, it's at
http://www.aristeia.com/BookErrata/ec++2e-errata_frames.html A detailed
list of changes (in the usual pseudo-HTML I use internally) is listed
below. In conjunction with the recent updates I made to the errata pages
for ESTL and MEC++, I'm pleased to report that all the problems I know
about in my books are now listed online.
Also, you can now find a PDF copy of my November 2002 CUJ article, "Class
Template, Member Template -- or Both?" at
http://www.aristeia.com/Papers/CUJ_Nov_2002.pdf
Enjoy!
Scott
[New EC++ Errata Entries]
DATE DATE
REPORTED WHO PAGES WHAT FIXED
-------- --- ----- ------------------------------------------------ --------
10/12/02 bj 12 Clarify whether the "ISO/ANSI sanctified version
of C" I refer to in the book is now C99, not C89.
4/10/03 wk 77 The final bullet point on the page is misleading.
C++ imposes constraints on how copy constructors
behave, so rather than writing that a copy
constructor defines what it means to pass by
value, I should say that it defines how an
object is passed by value.
10/20/02 kk 125 In final sentence of 2nd-to-last paragraph,
clarify that callers must use the array form of
delete.
9/26/02 mh 127 It would be better if someFamousAuthor returned
a const String instead of just a String. This
would be consistent with the advice of Item 21.
10/12/02 bj 135 In first para, note that the "C philosophy that
variables should be defined at the beginning of
a block" is for C prior to C99.
! 10/12/02 bj Item 41 The first design problem includes the ability to
create "stacks of stacks of strings," but the
given solution has a private copy constructor,
making it impossible to create a Stack of Stacks.
Interesting Comments:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- -----------------------------------------------------------
2/17/03 dyx Items "Our programming guidelines now recommend implementing
16-17 assignment operators like this:
T const& T::operator=( T other )
{
swap( other ); // non-throwing operation
return *this;
}
My reply:
This is fine (except for the const return type :-}), as
long as you make clear that this approach may be
needlessly expensive. For example, if all your data
members are of built in types, their assignments can't
throw, so doing swaps instead of assignments just burns
cycles needlessly. For large objects, you're talking
about duplicating an object's contents on the heap in
order to be able to perform the non-throwing swap.
There's nothing wrong with that, but my experience has
been that people advocating this approach to implementing
operator= often overlook the cost that it incurs. It's
exception safe, but it's often not cheap.
12/30/02 ma Item 19 It's true that virtual functions must be members, but one
can get virtual-acting behavior by having a non-virtual
function (possibly a non-member) call a virtual function.
This can be especially useful for functions like operator<<,
which must be non-members but can be made to act virtual by
internally calling a virtual function, e.g., one named
"print" or "write".
2/17/03 dys Item 19 Regarding the last bullet on page 88, dys writes:
The same rule should be used for other operators that
require a different class as a left-hand operand. For
example, CORBA uses "Any <<= Type", and for all classes
besides Any, operator<<=(T) is a non-member. I propose
the following change to your algorithm. Instead of
else if (f is operator>> or operator<<)
write
else if (f is an operator and needs another class as
its left-hand operand)
6/15/02 sdm Item 33 Randy Meyers (no relation) has a nice article on inlining
in C99, including an explanation of how the C99 rules differ
from those in C++, in the July 2002 issue of the
C/C++
Users Journal.
10/13/02 ya Item 36 ya writes: "Nonvirtual functions may call other functions
which are virtual. In that case, derived classes are indeed
presented with mandadory implementation, but only in the
highest, close-to-the-surface level. By overriding virtual
functions, the overall behavior of the nonvirtual function
can change in derived classes. Such usage of nonvirtuals is
very useful and is the basis for the 'template method'
design pattern."
This is true, but it's important to note that the
externally observable behavior of any function is defined by
its specification (i.e., it's interface), not by its
implementation. A nonvirtual implemented using template
method may behave differently for different derived classes,
but its behavior is still bounded by its specification.
Callers don't care whether the function is virtual or
nonvirtual. All they care about is that the function they
call exhibits the behavior its specification promises.
For implementers of derived classes, it's a different
story. They care very much about whether a base class
function is virtual or nonvirtual, because that affects what
they are allowed to vary. In Item 36, my remarks are
focused on the relationship between authors of base and
derived classes, not on the relationship between authors of
a class and clients of that class.
Friday, April 25, 2003
Saturday, April 19, 2003
ESTL and MEC++ Errata Pages Updated
I just updated the errata pages for Effective STL (ESTL) and for More
Effective C++ (MEC++). Here are links:
ESTL: http://www.aristeia.com/BookErrata/estl1e-errata.html
MEC++: http://www.aristeia.com/BookErrata/mec++-errata_frames.html
New entries for both books are below.
I hope to update the errata page for Effective C++ by the end of this
month. I'll let you know when I do.
Scott
----------------------------------------------------------------------
NEW ENTRIES FOR ESTL
----------------------------------------------------------------------
Bugs:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- ------------------------------------------------
2/ 1/03 fr 90 Missing colon at end of page. (The missing colon is
actually deliberate, but replacing "try" with "try
this:" will probably be clearer, so that's the change
I'll make.)
1/31/03 fr 91 Include an xref to Item 7 near the definition for
DereferenceLess for readers who are confused about
why DereferenceLess is a non-template struct
containing a templatized operator().
4/18/03 lz 157 In 1st line, "many elements" ==> "many elements
with a particular value"
2/26/03 shh 166 In the third bullet point on the page, "returns
true or false" ==> "returns true or false (or
something that can be implicitly converted to
true or false)."
2/26/03 shh 211 In line 5 of 4th prose para, "do this is with"
==> "do this with"
Interesting Comments:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- -----------------------------------------------------------
7/16/01 sdm Item 4 If you're interested in the technical and standardization
issues surrounding list::size, check out the July 2001
href="http://groups.google.com/groups?hl=en&lr=lang_en|lang_de&ie=UTF-8&oe=UTF-8\
&threadm=uZj47.33018%24WS4.5267300%40news6-win.server.ntlworld.com&rnum=1&prev=/\
groups%3Fhl%3Den%26lr%3Dlang_en%257Clang_de%26ie%3DUTF-8%26oe%3DUTF-8%26selm%3Du\
Zj47.33018%2524WS4.5267300%2540news6-win.server.ntlworld.com"
target="_blank">comp.std.c++
thread discussing the matter.
12/ 2/02 jr 46 The code at the bottom of this page has two shortcomings:
(1) though it works when iterating over an entire container,
it's incorrect if given an arbitrary end iterator, because
the call to erase will invalidate the end iterator; and (2)
because each call to erase shifts all succeeding elements
down by one, the linear-looking loop really runs in
quadratic time.
Here's code that addresses both problems, where endIt is the
arbitrary end iterator to be used and, for consistency,
beginIt is an arbitrary begin iterator to be used:
vector::iterator i = beginIt;
while(i!=endIt && !badValue(*i)) ++i;
vector::iterator j = i;
while(i!=end){
if(badValue(*i)) {
logFile<<"Erasing "<<*i<<'\n';
} else {
*j=*i;
++j;
}
++i;
}
c.erase(j, end);
Note that the second while loop is essentially a variant of
remove_if (see Item 32).
jr reports that in simple tests he performed comparing the
performance of this code with that on the bottom of page 46,
he saw speed improvements of 2-3 orders of magnitude.
2/22/03 fr Item 34 When using an algorithm expecting a sorted range (e.g.,
includes, set_union, etc.) on a standard associative
container (especially a map or multimap), it's important to
pass the correct comparison function. The easiest way to do
this is to pass the result of the value_comp member
function:
typedef multimap map1, map2;
...
bool subMultiset = includes(map1.begin(), map1.end(),
map2.begin(), map2.end(),
map1.value_comp());
This works for sets and multisets, too, because for those
types, value_comp returns the same thing as key_comp.
However, as noted in Item 44, for operations like
lower_bound, etc., it's generally better to use member
functions instead of algorithms when both are applicable.
2/20/03 fr 156 Because postincrement is less efficient than
preincrement, the if statement may be better
implemented as follows:
if (p(*begin)) {
*destBegin = *begin;
++destBegin;
}
2/26/03 shh 159 In order to make sure that the initial summary value
passed to accumulate is of the appropriate type, shh
suggests using this form:
Y sum = accumulate (
begin, // acts like a T*
end, // acts like a T*
static_cast(initValue) // initValue need not be of type
Y
);
----------------------------------------------------------------------
NEW ENTRIES FOR MEC++
----------------------------------------------------------------------
Bugs:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- ------------------------------------------------
8/14/02 sdm 66-67 The inheritance-based conversions described on
these pages apply only to public inheritance.
For non-public inheritance, things get a bit more
complicated. For details, including a rationale
for the behavior, consult the July 2001 CUJ
column by Jim Hyslop and Herb Sutter,
target="_blank">"Baseless Exceptions."
1/ 6/03 ais 67 In the examples near the top of the page, it
would be good to note that catch-by-value can
lead to the slicing problem, a topic that is
discussed on page 70 in Item 13.
12/ 6/02 ddg 99 In last sentence "When countChar returns," ==>
"When the statement containing the call to
countChar finishes executing,"
Effective C++ (MEC++). Here are links:
ESTL: http://www.aristeia.com/BookErrata/estl1e-errata.html
MEC++: http://www.aristeia.com/BookErrata/mec++-errata_frames.html
New entries for both books are below.
I hope to update the errata page for Effective C++ by the end of this
month. I'll let you know when I do.
Scott
----------------------------------------------------------------------
NEW ENTRIES FOR ESTL
----------------------------------------------------------------------
Bugs:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- ------------------------------------------------
2/ 1/03 fr 90 Missing colon at end of page. (The missing colon is
actually deliberate, but replacing "try" with "try
this:" will probably be clearer, so that's the change
I'll make.)
1/31/03 fr 91 Include an xref to Item 7 near the definition for
DereferenceLess for readers who are confused about
why DereferenceLess is a non-template struct
containing a templatized operator().
4/18/03 lz 157 In 1st line, "many elements" ==> "many elements
with a particular value"
2/26/03 shh 166 In the third bullet point on the page, "returns
true or false" ==> "returns true or false (or
something that can be implicitly converted to
true or false)."
2/26/03 shh 211 In line 5 of 4th prose para, "do this is with"
==> "do this with"
Interesting Comments:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- -----------------------------------------------------------
7/16/01 sdm Item 4 If you're interested in the technical and standardization
issues surrounding list::size, check out the July 2001
href="http://groups.google.com/groups?hl=en&lr=lang_en|lang_de&ie=UTF-8&oe=UTF-8\
&threadm=uZj47.33018%24WS4.5267300%40news6-win.server.ntlworld.com&rnum=1&prev=/\
groups%3Fhl%3Den%26lr%3Dlang_en%257Clang_de%26ie%3DUTF-8%26oe%3DUTF-8%26selm%3Du\
Zj47.33018%2524WS4.5267300%2540news6-win.server.ntlworld.com"
target="_blank">comp.std.c++
thread discussing the matter.
12/ 2/02 jr 46 The code at the bottom of this page has two shortcomings:
(1) though it works when iterating over an entire container,
it's incorrect if given an arbitrary end iterator, because
the call to erase will invalidate the end iterator; and (2)
because each call to erase shifts all succeeding elements
down by one, the linear-looking loop really runs in
quadratic time.
Here's code that addresses both problems, where endIt is the
arbitrary end iterator to be used and, for consistency,
beginIt is an arbitrary begin iterator to be used:
vector
while(i!=endIt && !badValue(*i)) ++i;
vector
while(i!=end){
if(badValue(*i)) {
logFile<<"Erasing "<<*i<<'\n';
} else {
*j=*i;
++j;
}
++i;
}
c.erase(j, end);
Note that the second while loop is essentially a variant of
remove_if (see Item 32).
jr reports that in simple tests he performed comparing the
performance of this code with that on the bottom of page 46,
he saw speed improvements of 2-3 orders of magnitude.
2/22/03 fr Item 34 When using an algorithm expecting a sorted range (e.g.,
includes, set_union, etc.) on a standard associative
container (especially a map or multimap), it's important to
pass the correct comparison function. The easiest way to do
this is to pass the result of the value_comp member
function:
typedef multimap
...
bool subMultiset = includes(map1.begin(), map1.end(),
map2.begin(), map2.end(),
map1.value_comp());
This works for sets and multisets, too, because for those
types, value_comp returns the same thing as key_comp.
However, as noted in Item 44, for operations like
lower_bound, etc., it's generally better to use member
functions instead of algorithms when both are applicable.
2/20/03 fr 156 Because postincrement is less efficient than
preincrement, the if statement may be better
implemented as follows:
if (p(*begin)) {
*destBegin = *begin;
++destBegin;
}
2/26/03 shh 159 In order to make sure that the initial summary value
passed to accumulate is of the appropriate type, shh
suggests using this form:
Y sum = accumulate (
begin, // acts like a T*
end, // acts like a T*
static_cast
Y
);
----------------------------------------------------------------------
NEW ENTRIES FOR MEC++
----------------------------------------------------------------------
Bugs:
DATE
REPORTED WHO PAGES WHAT
-------- --- ----- ------------------------------------------------
8/14/02 sdm 66-67 The inheritance-based conversions described on
these pages apply only to public inheritance.
For non-public inheritance, things get a bit more
complicated. For details, including a rationale
for the behavior, consult the July 2001 CUJ
column by Jim Hyslop and Herb Sutter,
target="_blank">"Baseless Exceptions."
1/ 6/03 ais 67 In the examples near the top of the page, it
would be good to note that catch-by-value can
lead to the slicing problem, a topic that is
discussed on page 70 in Item 13.
12/ 6/02 ddg 99 In last sentence "When countChar returns," ==>
"When the statement containing the call to
countChar finishes executing,"