home | C++ | FAQ | technical FAQ | publications | WG21 papers | TC++PL | Tour++ | Programming | D&E | bio | interviews | videos | quotes | applications | guidelines | compilers

Errata for 2nd and 3rd printings of The C++ Programming Language

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.

All comment and corrections are welcome.

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/


Errors and Clarifications

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:

home | C++ | FAQ | technical FAQ | publications | WG21 papers | TC++PL | Tour++ | Programming | D&E | bio | interviews | videos | quotes | applications | guidelines | compilers