C++ Template Metaprogramming — Errata

Last Updated:2007/01/21 22:47:20 CST

First, a big “thank you” to everyone who has reported problems so far:

Index to Errata and Corrections

Errata that Appear in all Printings

2   Traits and Type Manipulation

Page 17

Fredrik Hedman noted that the second example takes the log of a negative number, and that floats were being converted to ints, probably losing information. Therefore, change

int a = apply_fg(5.0f,  std::negate<float>(), log2);
int b = apply_fg(3.14f, log2, std::negate<float>());

to:

float a = apply_fg(5.0f,  std::negate<float>(), log2);
float b = apply_fg(-3.14f, log2, std::negate<float>());

Page 19

Jim Jowdy correctly points out that:

iter_swap in section 2.1.3

should actually be:

iter_swap in section 2.1.2

Page 22

The swap algorithm is in the <algorithm> header, not <utility>. Accordingly, change:

#include <utility>   // for swap

to:

#include <algorithm> // for swap

5   Sequences and Iterators

Pages 81-83, 110, 123, and 133

The use of i and j to denote iterator types in this chapter is slightly confusing. Accordingly, change all uses of the identifiers i and j to p and q, respectively.

6   Algorithms

Page 116

Jordan DeLong noticed that there are two extra commas in the second example, which should read:

template <class Seq>
struct biggest_float_as_double
{
    typedef typename mpl::replace<
        typename mpl::begin<Seq>::type
      , typename mpl::end<Seq>::type
      , float
      , double
    >::type replaced;

    typedef typename mpl::max_element<
        typename mpl::begin<replaced>::type
      , typename mpl::end<replaced>::type
      , mpl::less<mpl::sizeof_<_1>, mpl::sizeof_<_2> >
    >::type max_pos;

    typedef typename mpl::deref<max_pos>::type type;
};
Page 122

In Table 6.1, replace:

mpl::find_if<seq, T, pred>

with:

mpl::find_if<seq, pred>

Thanks to Robert Valkenburg for reporting this one.

8   Diagnostics

Page 173

Andrew Savige noted that in the third paragraph, last sentence,

GCC is one sucha compiler

should be changed to

GCC is one such compiler

10   Domain-Specific Embedded Languages

Page 240

Rob Stewart noticed that an em dash was misplaced.

The basic idea—that member functions—can be seen as free functions accepting an initial class argument is supported by languages such as Dylan, but once again, not by native C++.

should have been

The basic idea—that member functions can be seen as free functions accepting an initial class argument—is supported by languages such as Dylan, but once again, not by native C++.
Page 241
The word closures should be stricken from the 2nd paragraph; it was never supposed to be in that sentence to begin with. Thanks to Rob Stewart for noticing that there was something wrong here.

Appendix B   The typename and template Keywords

Page 318

Phillip Ngan noticed that

template class T> struct base;

should really be

template <class T> struct base;

Errata that appear in the First and Second Printings

Preface

Page xiii

To the end of the fourth paragraph, add:

If you need a hint, consider discussing the problem on the boost-users mailing list (see http://www.boost.org/more/mailing_lists.htm). The book's Web site, http://www.boost-consulting.com/mplbook, has a link to wiki pages containing answers to selected exercises.
Page xiv

Change:

These idioms have been applied to the copies of the book's examples that appear on the accompanying CD, but to avoid distracting the majority of readers they don't appear in the main text.

to:

To avoid distracting the majority of readers, these workarounds don't appear in the main text, but you can download patched code from the book's website, http://www.boost-consulting.com/mplbook.

1   Introduction

Page 4

Joshua Berne writes:

You claim that the results of binary<678> would make a strange sort of sense as 6x22 + 7x21 + 8x20. This, however, would only work if binary's template recursion was doing its composition using addition instead of bitwise |, which doesn't carry and so produces a far less sensical result.

Joshua is right; accordingly we changed the definition of binary<unsigned long N> to:

template <unsigned long N>
struct binary
{
    static unsigned const value
       = binary<N/10>::value * 2   // prepend higher bits
         + N%10;                   // to lowest bit
};

template <>                           // specialization
struct binary<0>                      // terminates recursion
{
    static unsigned const value = 0;
};

2   Traits and Type Manipulation

Page 24

Replace:

iter_swap_impl<use_swap>::do_it(*i1,*i2);

with

iter_swap_impl<use_swap>::do_it(i1,i2);

Thanks to Yutaka Leon Suematsu for pointing this problem out. Instantiating iter_swap on some appropriate types would have been enough for us to discover this flaw. Accordingly, you might want to append the following to chapter2/example17.cpp in your electronic copy of the book's code samples:

#include <vector>
#include <string>
int main()
{
    std::vector<bool> x(20);
    nonstd::iter_swap(x.begin(), x.end() - 1);
    std::vector<std::string> y(20);
    nonstd::iter_swap(y.begin(), y.end() - 1);
}
Page 35

In exercise 2-2, change:

struct A {};

to:

struct A { virtual ~A() {} };

Thanks to Andy Moreton for reporting this error.

Page 35

In exercise 2-2, change:

B& b_ref = polymorphic_downcast<B&>(b_ref);

to:

B& b_ref = polymorphic_downcast<B&>(a_ref);

Thanks to Nicholas Mongeau for pointing out this error.

Page 35

In exercise 2-3, change:

// prints "long const*& volatile"
std::cout << type_descriptor<long const*& volatile>();

to

// prints "long const*&"
std::cout << type_descriptor<long const*&>();

Thanks to Bruno Martínez Aguerre for pointing out this error.

3   A Deeper Look at Metafunctions

Page 38

Michael Spiegel quite correctly points out that

angle

is a dimensionless (scalar) quantity, and accordingly should be replaced with the real seventh SI unit,

amount of substance.

likewise, on

Page 40
typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;

ought to be:

typedef mpl::vector_c<int,0,0,0,0,0,0,1> amount_of_substance;

Michael also points out, as a point of interest, that

Actually temperature is a derived unit, since you can convert from Kelvins into electron volts. But the SI system still keeps Kelvins as a separate unit.
Page 42

Change:

void transform(
    InputIterator1 start1, InputIterator2 finish1

to:

void transform(
    InputIterator1 start1, InputIterator1 finish1

Thanks to Rob Stewart for noticing this error.

Page 44
Ariel Badichi notes that “the result of m * a should probably be 49.0f, not 6.0f.” We think that's a very polite understatement.
Page 46

Larry Evans points out that

dependent names in apply's initializer list

should be changed to

dependent names in apply's base-specifier-list

4   Integral Type Wrappers and Operations

Page 62

Yutaka Leon Suematsu points out that we repeated our error from page 24 here. Replace:

iter_swap_impl<use_swap>::do_it(*i1,*i2);

with

iter_swap_impl<use_swap>::do_it(i1,i2);
Page 65

Rob Stewart suggested that we clarify the last sentence. Accordingly, change:

By taking advantage of the fact that Boost's integral metafunctions all supply a nested ::value, we can make yet another simplification to param_type:

to:

Since all of Boost's integral metafunctions supply a nested ::value, their specializations are valid integral type wrappers, and can be passed directly to mpl::if_, yielding another simplification:
Page 67

Rob Stewart asked for a clarification here as well:

Because touching a template's nested ::value forces instantiation, the logical expression boost::is_scalar<_1>::value || is_reference<_1>::value is evaluated immediately.

should be changed to:

Touching a template's nested ::value forces instantiation, so the logical expression boost::is_scalar<_1>::value || is_reference<_1>::value is evaluated immediately and only tests the properties of the placeholder type _1 itself.

5   Sequences and Iterators

Page 81

Change:

The author of an decrementable iterator

to:

The author of a decrementable iterator

Thanks to Rob Stewart for catching this one.

Page 87

Rob Stewart points out that we neglected to introduce S and t in Tables 5.8 and 5.9. Accordingly, change:

In Tables 5.8 and 5.9, k and k2 can be any type and pos1 and pos2 are iterators into S.

to:

In Tables 5.8 and 5.9, S is an Associative Sequence, pos1 and pos2 are iterators into S, and t, k, and k2 can be any type.
Page 88

Change:

The following expressions have constant complexity and return a new sequence that models all the same MPL sequence concepts as S does.

to:

The following expressions have constant complexity and return a new sequence S' that models all the same MPL sequence concepts as S does.

Thanks to Rob Stewart for this clarification.

Page 97
The heading for section 5.11.1 was supposed to be “Building a Tiny Sequence.” Thanks to Rob Stewart for noticing that the heading in the book seemed wrong.
Page 99

Change:

#include <boost/mpl/iterator_tag.hpp>

to:

#include <boost/mpl/iterator_tags.hpp>

Thanks to Larry Evans for noticing this one.

Page 104

Replace:

template <>
struct end_impl<tiny_tag>
{
    template <class Tiny>
    struct apply
      : eval_if<
            is_same<none,typename Tiny::t0>
          , int_<0>
          , eval_if<
                is_same<none,typename Tiny::t1>
              , int_<1>
              , eval_if<
                    is_same<none,typename Tiny::t2>
                  , int_<2>
                  , int_<3>
                >
            >
        >
    {};
};

with:

template <>
struct end_impl<tiny_tag>
{
    template <class Tiny>
    struct apply
    {
        typedef tiny_iterator<
            Tiny
          , typename eval_if<
                is_same<none,typename Tiny::t0>
              , int_<0>
              , eval_if<
                    is_same<none,typename Tiny::t1>
                  , int_<1>
                  , eval_if<
                        is_same<none,typename Tiny::t2>
                      , int_<2>
                      , int_<3>
                    >
                >
            >::type
        > type;
    };
};

Thanks to Jay Graham for pointing out this error.

Page 110

In exercise 5-8, change:

typedef mpl::lower_bound< fibonacci_series, int_<10> >::type n;
BOOST_STATIC_ASSERT( n::value == 8 );

typedef mpl::lower_bound< fibonacci_series, int_<50> >::type m;
BOOST_STATIC_ASSERT( m::value == 34 );

to:

typedef mpl::advance_c< mpl::begin<fibonacci_series>::type, 6 >::type i;
BOOST_STATIC_ASSERT( mpl::deref<i>::type::value == 8 );

typedef mpl::advance_c< i, 4 >::type j;
BOOST_STATIC_ASSERT( mpl::deref<j>::type::value == 55 );

Thanks to John Lynch and Ariel Badichi for pointing out a number of errors in the original exercise leading to this correction.

Page 110

In exercise 5-9, replace:

BOOST_STATIC_ASSERT( mpl::back<seq>::type::value == 21 );

with:

BOOST_STATIC_ASSERT( mpl::back<seq>::type::value == 13 );

Thanks to John Lynch and Ariel Badichi for reporting this one.

6   Algorithms

Page 116

Rob Stewart suggested that we change the comment in the first code example from:

// Given a nonempty sequence Seq, returns the largest type in an
// identical sequence where all instances of float have been
// replaced by double.

to:

// Given a nonempty sequence Seq, returns the largest type in a
// copy of Seq where all instances of float have been replaced
// by double.

We think that makes things much clearer.

Page 119

Rob Stewart pointed out that it was unclear what use of transform the 2nd sentence was referring to. Accordingly, change:

That's why we were able to use transform so effectively without specifying an inserter.

to:

That's why in Sections 3.1.4 and 3.1.5 we were able to use transform so effectively without specifying an inserter.
Pages 120-122
Rob Stewart noted that the “else:” line in the pseudocode examples should be moved left to match the indentation of the “if” line above it.
Page 122

In Table 6.1, replace:

mpl::find_if<seq, T, pred>

with:

mpl::find_if<seq, pred>

Thanks to Robert Valkenburg for reporting this one.

Page 123

Change “Logarithmic in invocations to compare” to “Logarithmic in invocations of compare” (twice). Also change “for all positions j” to “for all positions j in seq” (twice).

Thanks to Rob Stewart for these fixes.

Page 128
Rob Stewart notes that Output Iterators ought to be Output Iterator, singular. We note that it should also be set in the sans-serif typeface used for concepts.
Page 128

Replace

they fold the results into S using push_front or push_back.

with:

they fold the results into S using push_front or push_back, respectively.

Thanks to Rob Stewart for the suggestion.

Page 128

Rob Stewart notes that the sentence:

It follows that there's no reason an inserter (or a sequence building algorithm) needs to build new sequences; it can produce an arbitrary result depending on its function component.

bears some clarification. Accordingly, we've replaced it with:

Thus, a sequence building algorithm only actually builds sequences if its inserter's function component does: If, say, mpl::multiplies<_> is used instead, the algorithm's result will be a number and not a sequence.

7   Views and Iterator Adaptors

Page 132

Change:

In English, this means “return the size of the result of the element at the first position satisfying some condition,” where some condition is determined by the comparison predicate passed to lower_bound.

to:

In English, this means “return the size of the element at the first position satisfying some condition,” where some condition is determined by the comparison predicate passed to lower_bound.

Thanks to Ariel Badichi for noticing the extra words “of the result of.”

Page 136

Change:

[[s1, t1, u1...],
[s2, t2, u2...],
[s3, t3, u3...] ...]

to:

[[s1, t1, u1],
[s2, t2, u2],
[s3, t3, u3] ...]

John Lynch found this error.

Page 136

Change:

We can clean the code up using

to:

We can clean up the code using

Thanks to Rob Stewart for the suggestion.

Page 137

Rob Stewart points out that, contrary to our statement, our use of joint_view in the sort example actually doesn't yield the promised performace benefits.

Replace:

// return a sorted vector of all the elements from seq1 and seq2
typedef mpl::sort<
    mpl::copy<
        mpl::joint_view<seq1,seq2>
      , mpl::back_inserter<mpl::vector<> >
    >::type
>::type result;

with:

// return a sorted vector of all the elements from seq1 and seq2
typedef mpl::sort<
      mpl::joint_view<seq1,seq2>
    , mpl::less<_1,_2>
    , mpl::back_inserter<mpl::vector<> >
>::type result;
Page 138

Replace:

but let's try to firm the idea up a bit.

with:

but let's try to firm up the idea a bit.

Thanks to Rob Stewart for the suggestion.

Page 140

Ariel Badichi points out that the two instances of transform_view at the bottom of this page ought to be simply transform, and that in:

next<::zip_iterator<IteratorSeq> >

<: is a digraph for [. Insert a space to fix that line:

next< ::zip_iterator<IteratorSeq> >
Page 141

Ariel Badichi notes that exercise 7-1 incorrectly states that library implementation of zip_iterator employs transform_view instead of transform. This translates into the following new wording:

7-1.
Our implementation of zip_iterator uses transform to generate its nested ::type. Would using transform_view instead yield any advantages in this case?
Page 141

In exercise 7-2, replace:

Modify your zip_iterator so that its ::iterator_category

with:

Modify your zip_iterator so that its iterator category

Thanks to Ariel Badichi for reporting this one.

8   Diagnostics

Page 160

Brad Austin writes:

I believe your factorial example on page 160 has a number of issues.

Firstly I believe:

#include <boost/mpl/equal.hpp>

should be:

#include <boost/mpl/equal_to.hpp>

Secondly, the multiplies<> metafunction doesn't appear in the 1.32.0 version MPL Reference manual. Should this perhaps be changed to times<>?

Thirdly, I can't find documentation for multiplies<>, but assuming it works the same way as times<>, the Reference Manual indicates that its arguments are expected to meet the requirements of an integral constant. The second argument is:

factorial<typename mpl::prior<N>::type>

so factorial<> must model an integral constant. However factorial<> derives from an expression of the form:

mpl::eval_if<unspecified>

without adding any nested types or a value of its own, so factorial<> only models an integral constant if eval_if<unspecified> does, which it doesn't. (eval_if<unspecified>::type may, depending on the selected type, but eval_if<unspecified> itself is just a nullary metafunction.)

[...] even after fixing all of the previous issues, the original purpose of the example was to demonstrate how to use BOOST_STATIC_ASSERTION to cause the compiler to produce a meaningful error message when factorial is invoked with a negative argument, and this purpose is defeated (at least with my compiler, gcc 3.3) by the fact that invoking factorial with a negative argument still throws the compiler into a semi-infinite loop (as instantiating factorial<-1> instantiates factorial<-2>, which instantiates factorial<-3>, etc.). I believe this problem can be solved by changing equal_to to less_equal when comparing the argument against zero.

Thanks to Brad for pointing out all these issues! Here's the corrected version of the example in question:

#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/less_equal.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/prior.hpp>
#include <boost/static_assert.hpp>

namespace mpl = boost::mpl;

template <class N>
struct factorial
    : mpl::eval_if<
          mpl::less_equal<N,mpl::int_<0> >  // check N <= 0
        , mpl::int_<1>                      // 0! == 1
        , mpl::multiplies<                  // N! == N * (N-1)!
              N
            , factorial<typename mpl::prior<N>::type>
        >
    >::type
{
    BOOST_STATIC_ASSERT(N::value >= 0); // for nonnegative N
};
Page 172

Change:

the backtrace material shown as the ellipsis

to:

the backtrace material shown as ellipses

Thanks to Rob Stewart for noticing this one.

9   Crossing the Compile Time/Runtime Boundary

Page 177

Replace:

By using this second form, we can avoid building a whole new sequence of wrap specializations:

mpl::for_each<s, wrap<_1> >(print_type());

with:

By using this second form, we can avoid building a whole new sequence of wrap specializations. Thus,

mpl::for_each<
    mpl::transform<s, wrap<_1> >::type
>(print_type());

becomes:

mpl::for_each<s, wrap<_1> >(print_type());

Thanks to Rob Stewart for this suggestion.

Page 179

Replace

std::cout << x::value;  // handle integral wrappers

with

std::cout << T::value;  // handle integral wrappers

(we found this mistake ourselves!)

Page 180

In the primary f_impl template, print() is missing an argument. Thus, change:

template <class T>
static void print() { std::cout << T::value; }

to:

template <class T>
static void print(T) { std::cout << T::value; }

Thanks to Steve Hill for pointing out this omission.

Page 184

Change:

yielding value of type R:

to:

yielding a value of type R:

Thanks to Rob Stewart for reporting this one.

Steve Hill pointed out that we were not implementing the standard meaning of sin²(x), and Peter Weinert noticed that we were passing the wrong parameter type. Accordingly, change:

float sin_squared(double x) { return std::sin(std::sin(x)); }

to:

float sin_squared(float x) { float y=std::sin(x); return y*y; }

Peter Weinert also noticed an extra comma, so

input, input+5, output,

should be changed to:

input, input+5, output
Page 185

Peter Weinert found another issue.

input, input+5, seq2

should instead be written:

input, input+5, output
Page 186

Replace:

    int m4(typename metafunction3<T>::type p);
    ...
};

In this example, metaprograms are computing the value of m1, the type m2, and the parameter type of m4.

with:

    int m3(typename metafunction3<T>::type p);
    ...
};

In this example, metaprograms are computing the value of m1, the type m2, and the parameter type of m3.

Thanks to Rob Stewart for noticing the inconsistency.

Page 192

Larry Evans points out that:

requires knowing all the types following its type in the original sequence.

should actually be:

requires knowing all the types preceding its type in the original sequence.
Page 194

Change:

  • And pointers to data members

to:

  • Pointers to data members

Thanks to Rob Stewart for pointing out this one.

Page 195

Rob Stewart points out that the first code block on the page "is written as though it is a complete example, yet sin_squared() is not defined".

Replace:

inline float log2(float x) { return std::log(x)/std::log(2.0f); }

with:

float log2(float x) { return std::log(x)/std::log(2.0f); }
float sin_squared(float x) { return std::sin(std::sin(x)); }
Page 195
Ariel Badichi correctly suggests that the reference to exercise 9-4 ought to point to exercise 9-2 instead.
Page 196

Rob Stewart suggested that the format and wording of the "Type Erasure" section's opening paragraph can be confusing unless the reader already knowns where we are going with this. Consequently, we decided to make the following changes to the text.

Change:

To see what we mean, consider the following two expressions:

  1. compose<float>(std::negate<float>(), &sin_squared) with type:

    compose_fg<float,std::negate<float>,float(*)(float)>
    
  2. std::bind2nd(std::multiplies<float>(), 3.14159) with type:

    std::binder2nd<std::multiplies<float> >
    

Even though the results of these expressions have different types, they have one essential thing in common:

to:

To see what we mean, consider the following two runtime expressions:

  1. compose<float>(std::negate<float>(), &sin_squared)
  2. std::bind2nd(std::multiplies<float>(), 3.14159)

Even though the results of these expressions have different types (compose_fg<float,std::negate<float>,float(*)(float)> and std::binder2nd<std::multiplies<float> > correspondingly), they have one essential thing in common:

At the bottom of the page, after:

... though, your program may get bigger and sometimes even slower.

add:

In this section we'll show how a single non-templated function can operate on objects of all types conforming to a given interface—in this case, types whose instances are callable with a float yielding a float result.
Page 197

At the bottom, change:

We could integrate our discovery by adding a std::vector<int> member to screensaver, and changing next_screen to pass that as an additional argument to the customize function:

to:

We could integrate our discovery by adding a std::vector<int> member to screensaver, and changing next_screen to pass that as an additional argument to the get_seed function:

Ariel Badichi found this error.

Page 202

Change:

float operator()(float x) const
{
    return f(x);                    // Delegate
}

to:

float operator()(float x) const
{
    return this->f(x);              // Delegate
}

Thanks to Rob Stewart for pointing this out.

Page 211

Replace:

If the ::value of the enabling condition C is true, enable_if<C,T>::type will be T

with:

If the ::value of the enabling condition C is true, enable_if<C,T>::type would be T
Page 211

Change:

If we tried to compute the result type greedily, there would be error during overload resolution

to:

If we tried to compute the result type greedily, there would be an error during overload resolution

Thanks to Rob Stewart for reporting this error.

10   Domain-Specific Embedded Languages

Page 216
Ariel Badichi notes that “HMTL” should really be “HTML.”
Page 225

Replace:

You don't need to understand how to generated code works:

with:

You don't need to understand how the generated code works:
Page 225

Change:

It should be clear at this point that DSLs can make code more concise and easy-to-write.

to:

It should be clear at this point that DSLs can make code more concise and easier to write.

Thanks to Rob Stewart for this correction.

Page 237

Robert Valkenburg noticed what appears to be a bug in the Blitz++ documentation: There's an extra A(I,J+1,K) term in the stencil. We think it should probably read:

// a three-dimensional stencil (used in solving PDEs)
Range I(1,6), J(1,6), K(1,6);
B = (A(I,J,K) + A(I+1,J,K) + A(I-1,J,K)
 + A(I,J+1,K) + A(I,J-1,K) + A(I,J,K+1) + A(I,J,K-1)) / 7.0;
Page 238

Change:

Here, the expression slew(.799) would build a instance of class named_params<slew_tag, float, nil_t>

to:

Here, the expression slew(.799) would build an instance of class named_params<slew_tag, double, nil_t>

Note “a” versus “an” and “float” versus “double”. Thanks to Ariel Badichi for catching these errors.

Page 239

Larry Evans points out that

, name_type_is<char const*>  // slew_type = char const*

should be

, name_type_is<char const*>  // name_type = char const*
Page 240

Replace:

passed as the first argument to the std::multiplies<float>

with:

passed as the second argument to the std::multiplies<float>

Thanks to Rob Stewart for pointing out this error.

Page 241

Robert Valkenburg noticed that:

, bind2nd(std::minus<float>(), 1)

should really be:

, bind2nd(std::minus<float>(), 0.5)
Page 242

Replace:

  1. Print a sequence, replacing odd elements with periods.

With:

  1. Print a sequence, replacing even elements with periods.

11   A DSEL Design Walkthrough

Page 258
Ariel Badichi notes that “FMSs” should really be “FSMs.”
Page 261

Replace:

cd_detected(
     "louie, louie"

with:

cd_detected(
     "Louie, Louie"
Page 264

Replace:

there's no way we can ask the user to write that type down.

with:

there's no way we can ask the user to spell that type's name.
Page 266
Rob Stewart notes that “a FSM” should really be “an FSM.”
Page 266

Rob Stewart points out that the supposedly complete FSM declaration example we present is lacking declarations for the event classes.

Change:

// concrete FSM implementation
class player : public state_machine<player>
{

to:

struct play; struct open_close;                // events
struct cd_detected; struct pause; struct stop;

class player : public state_machine<player>
{

and remove the following comment lines to make room:

// concrete FSM implementation
    // the list of FSM states
    // transition table
Page 266

Bruno Martínez points out an erroneous space in:

void close_drawer(open_ close const&);

The line was meant to be:

void close_drawer(open_close const&);
Page 275

We wrote:

“In your Boost Installation, libs/mpl/book/chapter10/player.cpp contains a complete implementation of what we've expored here.”

That should actually read:

On this book's companion CD, examples/chapter11/example16.cpp contains a complete implementation of what we've expored here.
Page 275

Yutaka Leon Suematsu points out that:

template <class Event>
int no_transition(Event const& e)

should be:

template <class Event>
int no_transition(int state, Event const& e)
Page 275
Ariel Badichi asked why no_transition was public. It doesn't need to be; protected would probably have been better. Accordingly, change public: to protected:.
Page 276

We wrote:

In the examples directory of the code that accompanies this book, player2.cpp illustrates a state_machine that dispatches using O(1) lookup into a static table of function pointers.

That should actually say:

You can download a state_machine that dispatches using O(1) lookup into a static table of function pointers from http://boost-consulting.com/mplbook/examples/player2.cpp

Appendix A   An Introduction to Preprocessor Metaprogramming

Page 286

Replace:

Eventually you will want to understand the reason for their existence and how they can be used to optimize preprocessing speed;

with:

Eventually you will want to understand how they can be used to optimize preprocessing speed;
Page 300

Replace:

template <class R, BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)>
struct signature<function<R( BOOST_PP_ENUM_PARAMS(n,A) )> >
  : mpl::BOOST_PP_CAT(vector,n)<
      R, BOOST_PP_ENUM_TRAILING_PARAMS(n,A)
    > {};

with:

template <class R BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)>
struct signature<function<R( BOOST_PP_ENUM_PARAMS(n,A) )> >
  : mpl::BOOST_PP_CAT(vector,n)<
      R BOOST_PP_ENUM_TRAILING_PARAMS(n,A)
    > {};

Thanks to Rob Stewart for reporting this one!

Page 302

Rob Stewart points out that:

Where s, r, and d appear they have

should be:

Where s, r, and d appear, they have

Appendix C   Compile-Time Performance

Page 324

Johan Råde writes:

The naive recursive algorithm for fibonnacci numbers does not have time complexity O(n^2). It is even worse than that. It is O(fibonacci(n)). And fibonacci(n) grows exponentially with n.

Johan is right! This translates into following two changes:

  1. Replace:

    Consider the classic recursive Fibonacci function, with O(n2) complexity:

    With:

    Consider the classic recursive Fibonacci function, with exponential complexity:

  2. Replace:

    The complexity of the compile-time fibonacci function is not O(n2), but O(n).

    With:

    The complexity of the compile-time fibonacci function is not O(exp(n)), but O(n).

Page 326

It's not exactly an erratum, but we wrote:

“...some associative sequences use function overload resolution to implement their lookup strategies. Overload resolution can have a nontrivial cost in the compiler, but we're not considering it.”

One thing we probably should have added there is that overload resolution seems in practice to be so much faster than template instantiation that when you have a choice, using overload resolution is a huge win. Otherwise, there would be little point in having associative sequence operations that are only O(1) if you only consider template instantiations but O(N), strictly speaking, if you consider overload resolution.

It would have been good to have included some tests showing that to be true; we'll try to do that for the next edition.

Page 327

Thomas Schell correctly points out that the changes we make to the fibonacci template are not enough to support our claim that

“With STEP == 1, though, we ensure that the computation of fibonacci<N-2,...>::value never invokes fibonacci with an argument set that's been used before.”

Thomas writes:

I think that's incorrect, see the counter-example below. There are 2 occurences of fib<1,2>::val. For increasing N there are even more occurences of same argument sets.

fib<4,4>::val - fib<3,3>::val - fib<2,2>::val - fib<1,1>
               |               |               + fib<0,1>
               |               + fib<1,2>::val
               |
               + fib<2,3>::val - fib<1,2>::val
                               + fib<0,2>::val

The example needs to be rewritten to sustain the claim, then the corresponding tests need to be re-run and the graph re-done.

Appendix D   MPL Portability Summary

Pages 343-344
Scott Meyers noticed that we used the term “ETI” without defining it. ETI stands for “Early Template Instantiation;” you can find an explanation on the book's companion CD or at http://www.boost.org/libs/mpl/doc/tutorial/eti.html.

Bibliography

Page 345
The links in the bibliographic references for [AS01a] and [AS01b] are wrong. They should be http://www.boost-consulting.com/writing/iterator_adaptors0.pdf and http://www.boost-consulting.com/writing/iterator_adaptors0.html, respectively.
Page 345
Jin-ho Park noticed that Jeremy Siek's last name was misspelled as “Seik”… twice!
Page 345
Replace “Curiosly” with "Curiously."
Page 348

Jin-ho Park writes:

the item [Veld04] has a wrong internet URL. It should be http://www.cs.chalmers.se/~tveldhui/papers/2004/dissertation.pdf

Note the character ‘l’ in “/~tveldhui/.”

Companion CD

Missing Workarounds for Broken Compilers
See the Preface errata.
chapter1/example6.cpp

Ariel Badichi notes that str[0] is accessed without checking whether the string is empty or not. If the input contains a blank line, it will invoke undefined behavior. Accordingly, change:

if (str[0] == 'q' || str[0] == 'Q')

to:

if (!str.empty() && (str[0] == 'q' || str[0] == 'Q'))

Index

Rob Stewart writes:

filter_view is found on pages 137 and 274, but those aren't in the index.