Modified November 6, 2013.
Errata for Bjarne Stroustrup: The C++ Programming Language (4th Edition), Addison-Wesley, 2013. ISBN 0-321-56384-2. Errata for the 2nd and 3rd printings.
There seem to be a wide variety of opinion how much errata should be posted and how it should be presented. I have decided to post only errata that in my opinion may affect comprehension. When I posted all errata (however trivial) for earlier editions, many complained that the important were impossible to find among the trivial.
I do correct all typos and minor gramatical issues in future printings, and I may insert minor clarifications in the book. I just don't post them all as errata.
I do not plan to update the posted four DRAFT chapters of ``A Tour of C++'', please comment on the (improved) book version of those chapters.
I sometimes use the terse notation: s/old text/new text/
Chapter 3:
pg 74, top: s/new double[sz]/new double[a.sz]/
pg 77, top: s/init()/init(1000)/
pg 79: The Vector iterator examle should handle the empty Vector:
template<typename T>
T* begin(Vector<T>& x)
{
return x.size() ? &x[0] : nullptr; // pointer to first element or nullptr
}
template<typename T>
T* end(Vector<T>& x)
{
return begin(x)+x.size(); // pointer to one-past-last element
}
pg 82, bottom: The function f() should be before the template f().
Chapter 4:
pg 94, bottom: /setf/setstate/
pg 97: s/book[ph.size()].number/book[book.size()].number/
pg 102: remove deque<T> and forward_list.
Chapter 5:
pg 119: s/while(mcond.wait(lock)) ...;/mcond.wait(lock);/
pg 125: s/decltype(*beg)/Value_type<For>/ reparing the use of decltype would take more space than I have here.
pg 127: Remove the last line (the standard didn't adopt the Boost << for patterns).
Chapter 6:
pg 143: s/160/140/ twice
pg 145: s/exactly 8 bits/exactly 16 bits/
pg 151: s/aligas(X)/alignas(X) char/
pg 163: s/for (vector/for (typename vector/
pg 163: Improved last example:
void f(complex<double> d)
{
// ...
auto max = d+7; // fine: max is a complex<double>
double min = d-9; // oops! we assumed that d was a scalar
// ...
}
pg 167: s/vector<string>& v2/vector<string> v2/
pg 168: s/stdint/cstdint/
Chapter 7:
Chapter 8:
pg 209, corrected output operator:
ostream& operator<<(ostream& os, Point p)
{
return os << '{' << p.x << ',' << p.y << '}';
}
pg 220, corrected try_to_print():
void try_to_print(Printer_flags x)
{
if ((x&Printer_flags::acknowledge)!=none) {
// ...
}
else if ((x&Printer_flags::busy)!=none) {
// ...
}
else if ((x&(Printer_flags::out_of_black|Printer_flags::out_of_color))!=none) {
// we are out of black or we are out of color
// ...
}
}
Chapter 9:
pg 237: last example: s/if (!prime/if (prime/
Chapter 10:
pg 249: s/return ct=={/return ct={/
pg 251, better handling of names:
default: // NAME, NAME=, or error
if (isalpha(ch)) {
ct.string_value = ch;
while (ip->get(ch))
if (isalnum(ch))
ct.string_value += ch; // append ch to end of string_value
else {
ip->putback(ch);
break;
}
ct.kind = Kind::name;
return ct;
}
pg 258: and means &&
pg 258: or means ||
pg 267: s/Similarly, floating-point promotion is used to create doubles out of floats//
pg 267: s/or long double//
Chapter 11:
pg 275: s/std::runtime_error{"unexpected nullptr}/throw std::runtime_error{"unexpected nullptr"}/
pg 280: s/smart_ptr/shared_ptr/
pg 297, improved example:
void g(double y)
{
auto z0 = [&]{ f(y); }; // return type is void
auto z1 = [=](int x){ return x+y; }; // return type is double
auto z2 = [y]{ if (y) return 1; else return 2; }; // error: body too complicated
// for return type deduction
auto z3 =[y]() { return (y) ? 1 : 2; }; // return type is int
auto z4 = [y]()->int { if (y) return 1; else return 2; }; // OK: explicit return type
}
pg 298, improved example:
void g(string& vs1, string& vs2)
{
auto rev = [](char* b, char* e) { while (1<e-b) swap(*b++,*--e); };
rev(&s1[0],&s1[0]+s1.size());
rev(&s2[0],&s2[0]+s2.size());
}
pg 298: s/(int a)/(double a)/
Chapter 12:
pg 312, corrected Fibbonacci example:
constexpr int ftbl[] { 0, 1, 1, 2, 3, 5, 8, 13 };
constexpr int fib(int n)
{
return (n<sizeof(ftbl)/sizeof(*ftbl)) ? ftbl[n] : fib(n-2)+fib(n-1);
}
pp 338-339, improved example:
#define printx(x) cout << #x " = " << x << '\n'
int a = 7;
string str = "asdf";
void f()
{
printx(a); // cout << "a" " = " << a << '\en';
printx(str); // cout << "str" " = " << str << '\en';
}
pg 340: s/yyyy:mm:dd/ Mm dd yyyy/
pg 340: s/__FUNC__/__func__/
Chapter 13:
pp 379-385, the extended vector example had suffered version skew:
template<typename T, typename A = allocator<T>>
struct vector_base { // memory structure for vector
A alloc; // allocator
T* elem; // start of allocation
T* space; // end of element sequence, start of space allocated for possible expansion
T* last; // end of allocated space
vector_base(const A& a, typename A::size_type n, typename A::size_type m =0)
: alloc{a}, elem{alloc.allocate(n+m)}, space{elem+n}, last{elem+n+m} { }
~vector_base() { alloc.deallocate(elem,last-elem); }
vector_base(const vector_base&) = delete; // no copy operations
vector_base& operator=(const vector_base&) = delete;
vector_base(vector_base&&); // move operations
vector_base& operator=(vector_base&&);
};
template<typename T, typename A>
vector_base<T,A>::vector_base(vector_base&& a)
: alloc{a.alloc},
elem{a.elem},
space{a.space},
last{a.last}
{
a.elem = a.space = a.last = nullptr; // no longer owns any memory
}
template<typename T, typename A>
vector_base<T,A>& vector_base<T,A>::operator=(vector_base&& a)
{
swap(*this,a);
return *this;
}
template<typename T, typename A = allocator<T> >
class vector {
vector_base<T,A> vb; // the data is here
void destroy_elements();
public:
using size_type = typename A::size_type ;
explicit vector(size_type n, const T& val = T{}, const A& a = A{});
vector(const vector& a); // copy constructor
vector& operator=(const vector& a); // copy assignment
vector(vector&& a); // move constructor
vector& operator=(vector&& a); // move assignment
~vector() { destroy_elements(); }
size_type size() const { return vb.space-vb.elem; }
size_type capacity() const { return vb.last-vb.elem; }
void reserve(size_type); // increase capacity
void resize(size_type, const T& ={}); // change the number of elements
void clear() { resize(0); } // make the vector empty
void push_back(const T&); // add an element at the end
// ...
};
template<typename T, typename A>
void vector<T,A>::destroy_elements()
{
for (T* p = vb.elem; p!=vb.space; ++p)
p->~T(); // destroy element (_ctor.dtor.explicit_)
vb.space=vb.elem;
}
template<typename T, typename A>
vector<T,A>::vector(size_type n, const T& val, const A& a)
:vb{a,n} // allocate space for n elements
{
uninitialized_fill(vb.elem,vb.elem+n,val); // make n copies of val
}
template<typename T, typename A>
vector<T,A>::vector(const vector<T,A>& a)
:vb{a.vb.alloc,a.size()}
{
uninitialized_copy(a.begin(),a.end(),vb.elem);
}
template<typename T, typename A>
vector<T,A>::vector(vector&& a) // move constructor
:vb{move(a.vb)} // transfer ownership
{
}
template<typename T, typename A>
vector<T,A>& vector<T,A>::operator=(vector&& a) // move assignment
{
clear(); // destroy elements
swap(vb,a.vb); // transfer ownership
return *this;
}
template<typename T, typename A>
vector<T,A>& vector<T,A>::operator=(const vector& a) // offers the strong guarantee (_except.guarantees_)
{
vector_base<T,A> b {a.vb.alloc,a.size()}; // get memory
uninitialized_copy(a.begin(),a.end(),b.elem); // copy elements
destroy_elements(); // destroy old elements
swap(vb,b); // transfer ownership
return *this; // implicitly destroy the old value
}
template<typename T, typename A>
vector<T,A>& vector<T,A>::operator=(const vector& a) // offers the strong guarantee (_except.guarantees_)
{
vector temp {a}; // copy allocator
swap(*this,temp); // swap representations
return *this;
}
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc) // flawed first attempt
{
if (newalloc<=capacity()) return; // never decrease allocation
vector<T,A> v(newalloc); // make a vector with the new size
copy(vb.elem,vb.elem+size(),v.begin()); // copy elements
vb.space = size();
swap(*this,v); // install new value
} // implicitly release old value
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return; // never decrease allocation
vector_base<T,A> b {vb.alloc,size(),newalloc-size()}; // get new space
uninitialized_move(vb.elem,vb.elem+size(),b.elem); // move elements
swap(vb,b); // install new base
} // implicitly release old space
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
using T = Value_type<Out>; // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
for (; b!=e; ++b,++oo) {
new(static_cast<void*>(&*oo)) T{move(*b)}; // move construct
b->~T(); // destroy
}
return oo;
}
template<typename T, typename A>
void vector<T,A>::resize(size_type newsize, const T& val)
{
reserve(newsize);
if (size()<newsize)
uninitialized_fill(vb.elem+size(),vb.elem+newsize,val); // construct new elements
else
destroy(vb.elem+newsize,vb.elem+size()); // destroy surplus elements
vb.space = vb.last = vb.elem+newsize;
}
template<typename In>
void destroy(In b, In e)
{
for (; b!=e; ++b) // destroy [b:e)
b->~Value_type<In>(); // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
}
template< class T, typename A>
void vector<T,A>::push_back(const T& val)
{
if (capacity()==size()) // no more free space; relocate:
reserve(size()?2*size():8); // grow or start with 8
vb.alloc.construct(&vb.elem[size()],val); // add val at end
++vb.space; // increment size
}
Chapter 14:
pg 392: s/expr()/expr(true)/
pg 397: s/void mf();/void mf(N::S);/
Chapter 15:
pg 438:
namespace Error {
extern int no_of_errors;
double error(const string& s);
}
Chapter 16:
pg 469: s/void g(const T*);/void g(Node*);/
Chapter 17:
pg 488, improved example:
class Nonlocal {
public:
// ...
void destroy() { delete this; } // explicit destruction
private:
// ...
~Nonlocal(); // don't destroy implicitly
};
void user()
{
Nonlocal x; // error: cannot destroy a Nonlocal
Nonlocal* p = new Nonlocal; // OK
// ...
delete p; // error: cannot destroy a Nonlocal
p.destroy(); // OK
}
pg 488: s/class Circle {/class Circle : public Shape {/
pg 503, bottom paragraph: An object is considered constructed after its first (possibly delegated-to) constructor has completed.
pp 512-513, corrected and simplified copy-on-write example:
class Image {
public:
// ...
Image(const Image& a); // copy constructor
// ...
void write_block(Descriptor);
// ...
private:
Representation* clone(); // copy *rep
shared_ptr<Representation> rep; // potentially share
};
Image::Image(const Image& a) // do shallow copy
:rep{a.rep} // a.rep now has two users
{
}
void Image::write_block(Descriptor d)
{
if (rep.use_count() > 1)
rep = shared_ptr<Representation>{clone()};
// ... now we can safely write to our own copy of rep ...
}
pg 522: s/T&/Handle&/
Chapter 18:
Chapter 19:
pg 555: s/return *++ptr;/return *this;/
pg 556: s/Ptr_to_T p(&v[0],v,200);/Ptr<T> p(&v[0],v);/
pg 560: Improved ternary literal example:
constexpr int ipow(int x, int n) // x to the power of n
{
return n>0?x*ipow(x,n-1):1;
}
template<char...> struct helper; // unused general template (primary template; _spec.special.primary_)
template<char c>
struct helper<c> { // handle one digit
static_assert('0'<=c&&c<'3',"not a ternary digit");
static constexpr int value() { return c-'0'; }
};
template<char c, char... tail>
struct helper<c, tail...> { // handle several digits
static_assert('0'<=c&&c<'3',"not a ternary digit");
static constexpr int value() { return (c-'0')*ipow(3,sizeof...(tail)) + helper<tail...>::value(); }
};
template<char... chars>
constexpr int operator"" _b3()
{
return helper<chars...>::value();
}
pg 572: s/i++/++i/ and s/j++/++j/
Chapter 20:
Chapter 21:
Chapter 22:
Chapter 23:
Chapter 24:
Chapter 25:
Chapter 26:
Chapter 27:
pg 765: s/enable_if()/enable_if/
Chapter 28:
Chapter 29:
Chapter 30:
Chapter 31:
Chapter 32:
pg 935: s/[p:p+(e-b))/[p:p+(e2-b2))/ twice
pg 943: s/smaller of b2/smaller of e2/ twice
Chapter 33:
Chapter 34:
pg 988, for the x=*up entry: s/x=up.cp/x=*up.cp/
Chapter 35:
Chapter 36:
Chapter 37:
pg 1059: s/(s1,m,p2)/(s,m,pat)/
pg 1067: s/A regex_iterator is a bidiractional iterator/A regex_iterator is an adaptor for a bidirectional iterator/
Chapter 38:
pg 1084: add the declaration "char c1, c2,c3;" to read_pair()
Chapter 39:
pg 1141: s/os<<to_string(d,os.getloc())/os<<d.to_string(os.getloc())/
Chapter 40:
Chapter 41:
Chapter 42:
pg 1227: s/_lock_t/_lock/ three times
pg 1228: s/unique_locl<defer_lock_t,mutex> lck1 {mtx};/unique_locl<mutex> lck1 {mtx,defer_lock};/ twice
pg 1228: s/unique_locl<defer_lock_t,mutex> lck2 {mtx};/unique_locl<mutex> lck2 {mtx,defer_lock};/ twice
pg 1229: s/unique_lock lck1 {m1,defer_lock_t};/unique_lock<mutex> lck {m1,defer_lock};/ s/unique_lock lck2 {m1,defer_lock_t};/unique_lock<mutex> lck {m2,defer_lock};/
Chapter 43:
Chapter 44: