Josuttis C++ Move Semantics  C++ Move Semantics - The Complete Guide: Errata of First Edition

C++ Move Semantics - The Complete Guide

Errata of the First Edition

April 19, 2022

This is the errata of the 1st printing of the book C++ Move Semantics - The Complete Guide by Nicolai M. Josuttis.
It covers all errors that were found.

The errata is organized in the following way:
- The first part lists technical errors
- The second part lists typos

The latest electronic version usually covers all issues of this errata.


Errors

Page 65, last two figures of 4.3.2 Initialize Members via Moved Parameters Passed by Value
Fixed: 2021-09-22

After the value of l was moved, its member len also becomes 0 to signal that the value was moved away.

Page 120, 7.3.1 Checking for noexcept Move Constructors in Abstract Base Classes:
Fixed: 2020-10-27

The helper class for the type trait is_nothrow_movable defined in poly/isnothrowmovable.hpp.html also has to implement all pure virtual functions (see the current fixed code online).

Page 146, 9.2.2 std::forward<>(): std::forward<>() for Calling Member Functions:
Fixed: 2020-10-27

s/ std::forward<T>(x).getName(); // calls getName() && (OK, because we no longer need x) / std::forward<T>(x).getName(); // calls getName() && for rvalues (OK, no longer need x) /

Page 157, 10.1.2 Universal References in Detail: Value Category Dependent Code:
Fixed: 2020-10-27

The comments in the then and else part of the code example were swapped. Of course, std::is_lvalue_reference<> checks for lvalues (having no move semantics).

Page 158, 10.1.3 Universal References of Specific Types:
Fixed: 2022-04-19

Instead of uing type traits, you better use concepts in rquires clauses:

s/ requires std::is_same_v<std::remove_cvref_t<T>, std::string> / requires std::same_as<std::remove_cvref_t<T>, std::string> /

s/ requires std::is_convertible_v<T, std::string> / requires std::convertible_to<T, std::string> /

s/ requires std::is_constructible_v<std::string, T> / requires std::constructible_from<std::string, T> /

You can even use concepts as type constraints instead of typenme. For example,

 template<std::convertible_to<std::string> T>
  void processString(T&&) {
  ...
 }

Page 160, 10.2.2 Rvalue References of Parameters in Class Templates:
Fixed: 2022-04-19

In generic/universalclass.cpp insert() takes an rvalue reference, so it should move the parameter into values:

 void insert(T&& val) {
  values.push_back(std::move(val));
 }

Page 161, 10.2.3 Rvalue References of Parameters in Full Specializations
Fixed: 2021-11-08

Note that you actually even all four possible full specializations (include one for const rvalue references). Otherwise there can always be a case where the primary template is used instead (e.g. if the latter is missing, the primary template will be called if you pass a const string marked with std::move()).

template<typename T> // primary template
void foo(T&& arg);   // - arg is a universal reference
...

template<>                        // full specialization for const lvalues
void foo(const std::string& arg); // - arg is not a universal reference

template<>                        // full specialization for non-const lvalues
void foo(std::string& arg);       // - arg is not a universal reference

template<>                        // full specialization for non-const rvalues
void foo(std::string&& arg);      // - arg is not a universal reference

template<>                        // full specialization for const rvalues
void foo(const std::string& arg); // - arg is not a universal reference

Page 166, 10.3.2 Conflicting Template Parameter Deduction with Universal References:
Fixed: 2020-10-27

s/ insert(coll, s); // OK, T deduced as std::string / insert(coll, s); // OK, with T deduced as std::string& vec now binds to coll /

Page 173, 11.1.1 Default Perfect Passing in Detail:
Fixed: 2020-10-27

s/ process(computeLRef(str)); // calls process(const std::string&) / process(computeLRef(str)); // calls process(std::string&) /

Page 219, 15.3.1 Move Semantics for Pairs:
Fixed: 2022-04-19

The example:

 std::pair<const char*, std::string> p5{"answer", "is 42"};
 auto p6{std::move(p5)};

should better be:

 std::pair<const char*, std::string> p5{"path", "tmp/sub"};
 std::pair<std::string, std::filesystem::path> p6{std::move(p5)};

to have the following more compelling output:

 p5: path/
 p6: path/"tmp/sub"


Typos

Page 32, 2.5 Passing by Value:

s/ value of from the passed argument / value from the passed argument /

Page 54, 3.3.7 Moving for Members with Disabled Move Semantics:

s/ deleted for of a type / deleted for a type /

s/ for classes where the type is used / for classes that have members of this type /

Page 56, 3.3.8 Exact Rules for Generated Special Member Functions: Move Assignment Operator:

s/ the copy assignment operator approximately / the move assignment operator approximately/

Page 78, 4.4.2 Implementing a Polymorphic Derived Class: output for poly/polygon.cpp:

s/ 'TestPolygon' / 'Poly1' / (twice)

Page 81, 5.1.2 Return by Reference:

s/ which is a reference to a returned Person / which is a reference to a member of a returned Person /

Page 84, 5.2 Overloading on Qualifiers:

s/ qualifiers it is not allowed: / qualifiers is not allowed: /

Page 86, 5.3.1 Reference Qualifiers for Assignment Operators:

s/ so fare / so far /

Page 89, 6.1 Required and Guaranteed States of Moved-From Objects:

s/ This means that means you / This means that you /

Page 91, 6.1.2 Guaranteed States of Moved-From Objects:

s/ a.assign(s2); / s.assign(s2);/

s/ the end of its lifetime. the minimum guarantee / the end of its lifetime, the minimum guarantee /

Page 116, 7.2.1 Rules for Declaring Functions with noexcept:

s/ noexcept is an error (but not the other way around). / noexcept in a derived class is an error (but not the other way around). /

Page 117, 7.2.1 Rules for Declaring Functions with noexcept:

s/ Therefore, derived classes should only restrict exception guarantees further. / Therefore, derived classes should never relax exception guarantees. /

Page 120, 7.3 noexcept Declarations in Class Hierarchies

s/ do not throw on a move assignment. / do not throw on a move construction. /

Page 122, 7.3.1 Checking for noexcept Move Constructors in Abstract Base Classes:

s/ std::is_nothrow_movable_v / is_nothrow_movable_v / (twice)

Page 134, 8.3.1 Overload Resolution with Rvalue References

s/ the usual copy semantics is used, taking the argument by const&. / the usual copy semantics is used inside f(), taking the argument by const&. /

Page 148, 9.3.1 Rvalue References of Actual Types:

s/ is declare / is declared /

Page 149, 9.3.2 Rvalue References of Function Template Parameters:

s/ non-const lvalue c / non-const lvalue v /

Page 156, 10.1.2 Universal References in Detail

remove: does also know whether an lvalue or an rvalue was passed.

Page 157, 10.1.2 Universal References in Detail:

s/ if constexpr(std::is_lvalue_reference_t<T>) / if constexpr(std::is_lvalue_reference_v<T>) /

Page 174, 11.2.1 Type Deduction of auto&&:

s/ By rule, the type of arg / By rule, the type of ref /

Page 176, 11.2.2 Perfectly Forwarding an auto&& Reference:

s/ which is the effect of std::move(arg) / which is the effect of std::move(ref) /

Page 177, 11.3.1 Universal References and the Range-Based for Loop: Specification of the Range-Based for Loop:

s/ The reason we declare coll / The reason we declare range /

Page 177, 11.3.1 Universal References and the Range-Based for Loop: Using the Range-Based for Loop:

s/ to have to use a or non-const) reference. / you have to use a or non-const reference. /

Page 178, 11.3.1 Universal References and the Range-Based for Loop:

s/ elem = elem + elem; / elem = value; /

Page 181, Using auto&& in C++20 Function Declarations:

s/ void callFoo(auto&& val) { / void callFoo(auto&& arg) { / (twice)

Page 185, 12.2 decltype(auto):

s/ with then name / with the name /

Page 188, 12.2.2 Deferred Perfect Returning:

s/ even before C++20 / even before C++17/

Page 188, 12.2.2 Deferred Perfect Returning:

s/ For prvalues, it is like / For plain values, it is like /

Page 189/190, 12.2.3 Perfect Forwarding and Returning with Lambdas:

s/ decltype(auto) r = f(std::forward<decltype(args)>(args)...); / decltype(auto) ret = f(std::forward<decltype(args)>(args)...); / (twice)

s/ return (r); / return (ret); /

Page 193, Chapter 13 Move-Only Types

s/ represent a value or the “own” / represent a value or “own” /

Page 196, 13.1.3 Passing Move-Only Objects as Arguments:

s/ // ensure mos resource / // ensure mo's resource /

Page 203, 14.2 Removing Algorithms: lib/removeif.cpp

In the email addresses the @ characters are missing.

Page 206, 14.3.1 Move Iterators in Algorithms:

s/ that have a size of 4 / that have a size other than 4 /

Page 211, 15.1.1 String Assignments and Capacity:

After:

On all platforms, a moved-from string is usually empty. This applies even when the value is not stored in
externally allocated memory (so that we have to copy all characters). After:
  std::string s3{std::move(s1)};
  std::string s4{std::move(s2)};

remove these lines:

  - s1 capa: 15 ('')
    s2 capa: 15 ('')
    s3 capa: 15 ('short')
    s4 capa: 47 ('a string with an extraordinarily long value')

Page 224, 15.4.2 Move Semantics for std::unique_ptr<>:

s/ sink(std::move(up); / sink(std::move(up)); / (twice)

Page 225/226, 15.5.1 Moving IOStream Objects: lib/outfile.cpp

s/ void storeData(std::ofstream fstrm) // takes ownership (open file handle) / void storeData(std::ofstream fstrm) // takes ownership of file (but this might change) / (twice)

s/ storeData(std::move(outFile)); // store data (closes file but this might change) / storeData(std::move(outFile)); // closes file (but this might change) / (twice)


Home of the C++ Move Semantics book