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: