Scott Meyers
Modification History and Errata List for Effective Modern C++
Last Updated 8 January 2026
by Scott Meyers
What follows are my notes on what I've changed in Effective Modern C++ since its official publication in November 2014 (i.e., excluding "Early Release" and "Rough Cuts" versions) and what I believe may need to be changed in the future. Most of the changes (or prospective changes) are cosmetic and don't affect the technical content of the book. Entries that do affect technical material are preceded with an exclamation mark ("!") and are displayed in red.
Each entry includes the following information:
- Date Reported: The date the problem was brought to my attention.
- Who: The initials of the person who caused me to become aware of the problem. The initials "sdm" refer to me. The mapping from other initials to full names is at the bottom of this page.
- Platform: The format(s) in which the problem occurs, i.e., PDF, ePub, Kindle (mobi), or Safari Books Online. PDF includes print books. As a general rule, content problems affect all platforms, but formatting issues are platform-specific.
- Location: Where in the book the erratum exists. This is typically either the PDF page number(s) or the Item number.
- What: A description of the problem and the solution.
- Date Fixed: The date on which the problem was fixed. If no date is shown, it means it hasn't yet been addressed.
I am always interested in bug reports and suggestions for improvements to Effective Modern C++. To submit comments, send email to emc++@aristeia.com.
The following issues have been addressed in published versions of the book:
DATE DATE
REPORTED WHO PLATFORM LOCATION WHAT FIXED
-------- --- -------- -------- ---------------------------------------------------------- --------
1/13/15 sdm Digital Metadata Digital TOCs should included a link to the book's 4/ 2/15
copyright page.
12/ 7/14 kxh ePub on Metadata Author is shown as "(Unknown author)". 3/31/15
Marvin
12/16/14 rxk All All When words in code font are broken across lines, they're 5/19/15
sdm not hyphenated.
11/18/14 sdm Kindle All Font size in code displays should be increased to match 1/20/15
size in the rest of the book.
11/18/14 lxs Kindle All Inter-paragraph spacing should be increased. 1/20/15
11/28/14 kxa Kindle All Highlighted code text (i.e., red and/or italicized) is 4/14/15
1/ 7/15 bxy on invisible. Wide and narrow ellipses are swapped.
Nexus (Problems reported on Nexus 4, 5, 7, and 10.)
11/18/14 sdm PDF vii Item title for Item 8 is incorrectly indented. 1/20/15
4/17/15 sdm PDF xiv Twice in first para, bad line break between day and month 5/ 8/15
in a date.
4/17/15 sdm PDF xiv Near end of first para, sentence-ending period should 5/ 8/15
precede the parenthesis, not follow it.
4/17/15 sdm PDF xiv Add tyk for performing an extraordinarily thorough 1/25/16
post-publication review of the book.
1/ 9/16 sdm PDF xv Ashley Morgan Williams ==> Ashley Morgan Long 1/25/16
310
315
11/15/14 kxv PDF xv "reviwing" ==> "reviewing" 1/20/15
11/21/14 sdm Safari 5 In last line of first code display, final parenthesis in 3/31/15
Online comment wraps to next line.
! 1/30/15 txk PDF 12 The third paragraph (beginning with "These examples all 3/24/15
show lvalue reference parameters, but type deduction
works exactly the same way for rvalue reference
parameters") should be removed. When the type of param
in the template f on page 11 is changed to an rvalue
reference (i.e., to have type "T&&"), it becomes a
universal reference, and then the rules for Case 2
(on pages 13-14) apply.
11/22/14 sdm PDF 21-22 In November 2014, N3922 was adopted for draft C++17. 3/25/15
With this change to the language, it is no longer the
case that the following two statements do the same thing:
auto x3 = { 27 };
auto x4{ 27 };
Under N3922, x4 is of type int, not
std::initializer_list<int>. Some compilers have
already implemented this prospective language change.
I added a footnote to this effect.
3/18/16 gxk PDF 21 In the footnote added to resolve an earlier erratum, 1/ 5/17
remove the cross reference to Item 42.
12/19/16 sdm PDF 21 In footnote, "some compilers" => "many compilers". 1/ 5/17
3/26/15 sdm PDF 24 In first line, "Occasionally" should be followed by a 3/26/15
comma.
2/ 9/15 tyk PDF 24 Clarify that in authAndAccess, we're interested only in 1/25/16
28 supporting containers taking numeric index values.
(This excludes std::map and std::unordered_map, which
take indices of arbitrary types).
12/13/14 rxm PDF 25 I should mention that for functions with deduced return 3/25/15
types, if there is more than one return statement, all
return statements must yield the same deduced type.
3/27/15 sdm PDF 26-27 Bad page break between the comment and the code it applies 3/30/15
to.
1/22/16 sdm PDF 28 Improper indentation of "-> decltype(...)". 1/25/16
2/ 2/15 mxw PDF 28 Reworded last para on page to reflect that not all names 3/25/15
are lvalues. Also added weasel wording regarding "lvalue
expressions more complicated than names" to try to avoid
contradicting 7.1.6.2/4 bullet 1 without getting sucked
into the details.
2/17/15 mam PDF 31 When attempting to instantiate an undefined template, 3/24/15
Intel C++ 15.0.2 on Linux issues a diagnostic that
fails to mention the type being used for the
instantiation.
1/ 6/16 sdm PDF 31 Too much blank space above first code example. 1/25/16
12/ 1/14 daa PDF 33 "the type that all three compilers report for param are" ==> 1/20/15
"the type that all three compilers report for param is"
11/21/14 sdm PDF 34 "neither are IDEs or templates" ==> 1/20/15
"neither are IDEs nor templates"
11/17/14 txk PDF 34 Boost's web site is boost.org, not boost.com. 1/20/15
11/23/14 bbs PDF 37-38 In the dwim functions, it would be more ideomatic to use a 3/25/15
for loop instead of a while loop.
2/ 8/15 mxm PDF 38 In comment next to initialization of x3, 3/24/15
"x's value is well-defined" ==> "x3's value is well-defined"
1/ 6/17 sxa PDF 41 In last para, xref to Items 2 and 6 should include Item 3, 5/18/18
too.
2/ 8/17 sdm PDF 41 At beginning of 4th line of second para, "const" should be 1/ 5/17
italicized, i.e., it should start with
"<const std::string, int>". (This error exists only in the
PDF and printed versions of the book and only in the
8th-10th printings. Kindle and epub versions of the book
don't have this mistake, nor do earlier versions in PDF
and print.)
1/ 6/17 sxa PDF 45 At end of 2nd para, "Pantheon" => "pantheon". 5/18/18
1/25/15 txk PDF 47 In comment of penultimate code block, 3/24/15
"impliclitly" ==> "implicitly".
11/23/14 bbs PDF 47-48 In last para, I should make explicit my assumption that 3/30/15
the container isn't empty.
! 11/23/14 bbs PDF 48 Twice on page, "d * c.size()" ==> "d * (c.size() - 1)". 1/20/15
! 3/26/15 lxz PDF 48 The 1/20/15 fix for the code on this page resolved the 4/ 3/15
problem (present in early printings of the book) that when
d==1.0, index would be c.size(), hence out of
bounds. Unfortunately, it also resulted in index being
c.size()-1 (the last element of the container) only when
d==1.0.
2/18/15 vhg PDF 51 In second paragraph, I say that narrowing conversions 3/24/15
using braced initializers won't compile, but the
Standard requires only that compilers issue some kind of
diagnostic message. A warning fulfills this requirement,
and some compilers issue only warnings for at least some
narrowing conversions using braced initializers.
12/19/16 sdm PDF 54-55 First line of page 55 should be moved to page 54 to keep 1/ 5/17
the comment together.
! 11/20/16 cxl PDF 55 Comment on last line on page is incorrect: the 1/ 5/17
314 constructor for w5 is called with a one-element
std::initializer_list, not an empty one. For details,
consult this blog post.
2/12/15 sdm PDF 58 Formatting of Item title is incorrect: too much space 3/24/15
after the colon.
12/21/14 tjb PDF 61 lockAndCall implementations should include this line at 1/20/15
the beginning:
using MuxGuard = std::lock_guard<MuxType>;
4/21/16 gxp PDF 71 2nd para suggests that it's possible to specify the 1/ 5/17
underlying type only for unscoped enums. Reword.
2/23/15 mxk PDF 77 In next-to-last para, wchar_t, char16_t, and char32_t 3/24/15
are keywords, hence should not be std::-qualified.
7/16/16 axd PDF 77 Penultimate para fails to mention volatile-only overloads 1/ 5/17
(i.e., volatile void*, volatile char*).
11/17/15 rxh PDF 81 Of the three compilers I use for my testing (gcc, MSVC, 1/25/16
bxc Clang), only one (gcc) issues no warnings when full
sdm warnings are enabled (i.e. -Wall -Wsuggest-override).
11/12/16 hxb PDF 81-82 Derived::mf4 should be declared virtual. 1/ 5/17
11/23/14 bbs PDF 83-85 I should mention that if a member function is 3/26/15
reference-qualified, any other overload of the same
member function (i.e., matching name and parameter
types) must also be reference-qualified.
12/25/14 mxs PDF 85 A better way to have the rvalue reference overload of the 3/26/15
1/27/15 tyk "data" member function return an rvalue is to have it
return an rvalue reference. That would avoid the creation
of a temporary object for the return value, and it would
be consistent with the by-reference return of the original
"data" interface near the top of page 84.
11/12/16 hxb PDF 86 Missing space after first comma in page's first call to 1/ 5/17
88 std::find.
1/22/16 sdm PDF 88 Missing spaces around "->" in return type for cbegin 1/25/16
at bottom of page.
1/22/16 sdm PDF 89 In first para, incorrect font for final "r" in 1/25/16
"const_iterator".
1/ 4/15 jxz All PDF At end of sentence before code display, space between 1/20/15
pg 95 "seems" and "appropriate" is too wide. In ePub version,
there are two spaces, but in PDF and Mobi versions,
there's a single overly-wide space.
1/18/15 bbs PDF 99-100 In pow, exp should be declared unsigned, because the 1/25/16
algorithm works only for non-negative exponents.
! 12/24/14 akb PDF 105 Neither std::mutex nor std::atomics are move-only. They 3/25/15
106 are neither copyable nor movable, so classes containing
108 them are rendered both uncopyable and unmovable.
11/14/16 sdm PDF 105 In antepenultimate line on page (i.e., 3rd-to-last line— 1/ 5/17
I just wanted to use the word "antepenultimate" :-}),
"i.e," => "i.e.,". (Thanks to txb for correcting my
misspelling in this errata list of, sigh,
"antepenultimate".)
1/ 4/15 jxz PDF 106 It would be better to use std::hypot to compute the 3/25/15
distance from the origin, because (1) std::hypot does
exactly what's needed and is part of the standard
library and (2) as this blog post explains, it handles
potential overflow values much better than the code shown
in the book.
12/12/14 bxr PDF 107 "CacheValid" ==> "cacheValid". 1/20/15
! 12/12/14 bxr PDF 107 The text above the code display near the middle of the 1/20/15
page says that reversing the order of the assignments
solves the problem, but that's incorrect. Multiple threads
could still perform the expensive calculations before
cacheValid is set.
8/31/16 txb PDF 112 In antepenultimate line (see above) of paragraph above 1/ 5/17
first code display, "i.e," => "i.e.,".
3/25/15 gxs PDF 115 At end of second bullet in "Things to Remember," 3/25/15
"and a destructor" ==> "or a destructor".
1/24/17 djm PDF 115 Reword last sentence of bullet at top of page to improve 5/18/18
clarity. Also undo 3/25/15 change suggested by gxs,
i.e., at end of second bullet in "Things to Remember,"
"or a destructor" ==> "and a destructor".
3/ 6/17 djm PDF 115 In last sentence of third bullet in box, "with an 5/18/18
explicitly declared destructor" ==> "with an explicitly
declared copy operation or destructor".
6/ 2/15 lxz PDF 118 In fourth para, "it's not possible" ==> "it wasn't 10/21/15
possible". (In C++11, std::auto_ptrs can be stored in
containers.)
8/31/16 ryk PDF 118 "Item 17" in footer should be Item 18. 1/ 5/17
6/19/16 axd PDF 123 The book's claim that stateless function object deleters 1/ 5/17
incur no size penalty is true in practice, but it's not
guaranteed by the Standard. Reword. Be caseful to avoid
implying that stateless function objects in general have
no size.
6/23/15 dgw ePub 119 In iBooks, the beginnings or endings of some sentences 6/30/15
124 are missing. (This is a systemic problem. The list of
127 affected pages is probably not complete.)
135
138
6/ 2/15 lxz PDF 128 Text near end of the first paragraph implies that the 1/25/16
control block may not contain a weak count, but, as I note
on page 144 (in Item 21), the weak count is always present.
12/15/14 sdm PDF 129 "on page 115" ==> "on page 117". 1/20/15
1/18/15 bbs PDF 133 In first para, both copy- and move assignment can call for 1/25/16
reference count manipulations, because the target of a
move assignment must typically decrement the reference
count of the control block it currently points to.
6/10/17 tgm PDF 136 In line 7 of 2nd para, the final "s" in "std::weak_ptrs" is 5/18/18
in wrong font.
6/ 2/15 lxz PDF 139 Text in the second paragraph implies that std::make_unique 1/25/16
supports custom deleters, but that is not the case.
12/19/14 sdm PDF 151 Last line on page is indented too far. 1/20/15
1/29/15 tyk PDF 152-153 std::unique_ptr's move operations are noexcept, so 1/25/16
Widget's should be, too. Compiler-generated versions would
be noexcept automatically (see below), but user-declared
versions must be explicitly declared noexcept.
1/ 8/16 sdm PDF 153 In first para, clarify that compilers must be able to 1/25/16
generate code to destroy pImpl in the event of an
exception, even if the move constructor is noexcept. (The
source of this requirement is 12.6.2/10 in C++14, though
there's a proposal to relax it.)
! 12/18/14 ahp PDF 154 The copy operations shown on this page unconditionally 3/26/15
dereference pImpl, but an earlier move operation could
have left pImpl null. The code should check for nullness
and, if present, deal with it in a reasonable fashion.
12/18/14 ahp PDF 155 Using compiler-generated copy and move operations for 3/26/15
Widget containing a pImpl of type std::shared_ptr would
replace the value semantics of the class (i.e., deep copy)
with reference semantics (i.e., shallow copy).
11/12/16 hxb PDF 158 "Item 22" in footer should be Item 23. 1/ 5/17
1/ 4/15 jxz PDF 160 Near top of page, declaration of Annotation constructor 1/20/15
is missing terminating semicolon.
1/ 6/16 nxj PDF 163 Add "Things to Remember" bullet about how declaring 1/25/16
objects const disables move semantics for them.
12/16/14 rxm PDF 168 In second line on page, "args is zero or more" ==> 1/20/15
"params is zero or more".
1/24/17 djm PDF 168 In last sentence before box, "general relativity" => 5/18/18
"Einstein's theory of relativity".
2/11/15 mjs PDF 171 The last sentence of the second paragraph is misleading, 3/25/15
because for setters that are passed the "wrong" type
argument, the performance gap between (a) overloading on
lvalues and rvalues and (b) pass-by-universal-reference
is determined by several factors:
- Cost of constructing a temporary object of the "right"
type from the argument;
- Cost of moving the temporary into the data member;
- Cost of assigning the argument to the data member
(which may incur the costs of a temporary
construction and destruction).
I eliminated the sentence.
1/30/15 tyk PDF 171 Page should contain a cross reference to Item 41, which 1/25/16
has a fuller discussion of the tradeoffs between the use
of overloading and passing by universal reference.
7/ 7/15 mys PDF 179 In 2nd-to-last para, the overload taking a universal 1/25/16
reference would deduce T to be short&, not short, because
logAndAdd is being called with an lvalue (nameIdx).
12/15/14 sdm PDF 184 "on page 175" ==> "on page 177". 1/20/15
12/15/14 sdm PDF 185 "on page 177" ==> "on page 178". 1/20/15
4/15/15 sdm PDF 186 In first para, bad line break between "Item" and "26". 5/ 8/15
4/15/15 sdm PDF 187 In first para, bad line break between "Item" and "9". 5/ 8/15
12/15/14 sdm PDF 188 "on page 178" ==> "on page 180". 1/20/15
12/11/14 kxv PDF 192 "on page 206"==> "on page 183". Also, page number 1/20/15
should be a link.
2/22/15 vhg PDF 192 Final paragraph says that std::is_base_of<T,T>::value is 3/24/15
true, but that's the case only for class types. When T
is a built in type such as int, std::is_base_of<T,T>::value
is false.
4/ 6/15 sdm PDF 200 In first sentence, bad line break between "Item" and "9". 5/ 8/15
12/19/14 ahp PDF 210 This section also applies to declaration-only integral 3/24/15
static constexpr data members (i.e., to constexpr data
members as well as const data members).
11/12/16 hxb PDF 216 "Item 30" in footer should be Item 31. 1/ 5/17
12/17/14 rxm PDF 217 In first comment block, reference to Item 2 should be to 1/20/15
Item 5.
6/28/15 bvw PDF 226 C++11 code examples on these pages use std::make_unique, 1/25/16
229 but std::make_unique is a C++14 feature, not C++11
2/10/15 sxt PDF Item 33 Inside the lambda, the call to func should be eliminated, 3/30/15
because it plays no role in any of the examples.
11/14/16 sdm PDF 230 In second-to-last line preceding the last code display, 1/ 5/17
"i.e," => "i.e.,".
2/10/15 sxt PDF 231-232 For consistency with the rest of the Item, the lambda 3/30/15
parameter on page 231 should be named x, not param, and in
the variadic version on page 232, it should be named xs,
not params.
3/27/15 sdm PDF 232-233 Bad page break between the comment and the code it applies 3/30/15
to.
! 11/30/14 rxb PDF 234 The revised versions of setSoundB still invoke 1/20/15
235 steady_clock::now() when std::bind is called instead of
236 when the bind object is called. A proper fix requires
nesting a third call to std::bind inside the second one,
e.g., as follows for the C++14 version of the code:
auto setSoundB =
std::bind(setAlarm,
std::bind(std::plus<>(),
std::bind(steady_clock::now),
1h),
_1,
30s);
! 5/ 5/15 mam PDF 235 With the code at the top of the page, calls to 1/25/16
setSoundB won't compile, because
std::plus<steady_clock::time_point>'s operator()
declares two parameters of type steady_clock::time_point,
but we're passing a time_point and a duration.
The fix is to essentially implement C++14's generic
std::plus ourselves. Its templatized operator() function
accepts arguments of any type. So:
struct genericAdder {
template<typename T1, typename T2>
auto operator()(T1&& param1, T2&& param2)
-> decltype(std::forward<T1>(param1) + std::forward<T2>(param2))
{
return std::forward<T1>(param1) + std::forward<T2>(param2);
}
};
auto setSoundB =
std::bind(setAlarm,
std::bind(genericAdder(),
std::bind(steady_clock::now),
hours(1)),
_1,
seconds(30));
3/ 9/16 jxm PDF 235 The lambda used to initialize setSoundL is missing the 1/ 5/17
using directive for std::literals.
! 2/23/15 vhg PDF 239 Inside operator() for the closure boundPW, pw is const, 3/25/15
because operator() is a const member function. However,
PolyWidget::operator() is not const, so the call
"pw(param)" attempts to invoke a non-const
member function on a const object, and that's invalid.
3/23/15 sdm PDF 241 In first paragraph, 3/24/15
"compiler-writers" ==> "compiler writers".
2/ 8/15 tyk PDF 241 Clarify that std::async isn't synonymous with "task-based 1/25/16
programming," it's just an example of it.
1/ 6/15 oxb PDF 247 In paragraph above final code block, 1/20/15
"std::launch::deferred" ==> "std::future_status::deferred"
5/23/15 pxz Kindle ~8526 The end of the paragraph beginning with "If f runs 6/30/15
concurrently with the thread calling std::async (i.e., if
the launch policy chosen for f is std::launch::async)" is
not displayed. This problem was introduced in the
2015-05-08 release, i.e., it did not exist in prior
releases.
1/ 1/15 fxf PDF 251 In second comment block, reference to Item 2 should be to 1/20/15
Item 5.
7/26/16 mtb PDF 251 Clarify that terminating "program execution" means all the 1/ 5/17
threads, i.e., the entire process.
11/19/14 dxj PDF 257 "isappropriate" ==> "is appropriate" 12/12/14 (print and epub)
1/20/15 (PDF, mobi, and Safari)
4/ 9/15 sdm PDF 269 Footnote should replace blog post reference with 1/25/16
this followup blog post.
! 4/13/15 lxp PDF 270 Italicized comment opposite first "…" is incorrect. 1/25/16
ThreadRAII isn't being used in this example, so an
exception in this region would cause joinable std::threads
to be destroyed, hence would lead to program termination.
3/ 1/16 jsb PDF 271 2nd para of Item 40 says that "operations on [std::atomics] 1/ 5/17
behave as if they were inside a mutex-protected critical
section." For std::atomics using sequential consistency,
this is true, but for std::atomics using weaker
consistency models, it's only partially true. Regardless
of the consistency model, std::atomics appear to other
threads to change values atomically, but std::atomics
using weaker models permit some instruction reorderings
that would not be permitted if mutexes were used. (As the
footnote on page 274 implies, Effective Modern C++
assumes the use of sequential consistency throughout the
book.)
11/12/16 hxb PDF 272 In 4th line from bottom "e.g," => "e.g.,". 1/ 5/17
6/19/16 axd PDF 273 Penultimate para suggests that compilers can detect data 1/ 5/17
races and use the leeway from the resulting undefined
behavior to generate code to do anything they like. This
is misleading. Code generation occurs during compilation,
but data races occur at runtime. Reword.
7/17/15 sdm PDF 274 In 2nd line of footnote, "that use the syntax" ==> 10/21/15
"that uses the syntax".
12/15/14 sdm PDF 281 "Recall on page 2" ==> "Recall from page 4". 1/20/15
6/19/16 axd PDF 278 2nd para says "the code must be written like this", but 1/ 5/17
that's not true. Instead of invoking
std::atomic<int>::store on y, assignment can be used:
y = x.load(); // same as y.store(x.load());
1/11/15 pxv PDF 283 In final paragraph, "addName will be copy constructed" ==> 1/20/15
"newName will be copy constructed".
1/23/15 kxv PDF 289 Near middle of page, saying "changeTo’s use of assignment 3/25/15
290 to copy the parameter newPwd probably causes that
function’s pass-by-value strategy to explode in cost" is
misleading. It suggests that the problem is that changeTo
uses assignment, but that's how changeTo has to be
implemented. The real problem is that changeTo uses pass
by value for its parameter. At the bottom of the page, I
show how using pass by reference-to-const eliminates the
problem.
1/23/15 kxv PDF 292 In the "Things to Remember" box, the second bullet point 3/25/15
is unclear and should be reworded.
11/24/14 sdm PDF 299 Line 6 is unnecessarily indented. 1/20/15
11/25/14 sdm PDF 300 In the text following first code fragment, the constraint 3/24/15
on the pointer passed to the std::regex constructor is
incorrect. It's that the pointer be non-null, not that it
point to a valid regular expression. (Null pointers yield
undefined behavior. Pointers pointing to invalid regular
expressions yield exceptions.)
8/ 3/15 sdm PDF 303 Allusion to "Adventure" should refer to page 292, not 10/21/15
page 295.
9/ 7/15 kyh PDF 308 In second column, entries for std::basic_ios::basic_ios 10/21/15
and std::basic_ios::operator= should refer only to page
75, not both 75 and 160.
2/19/18 sdm PDF 310 In column 2, eliminate fourth line from bottom of page. 5/18/18
6/19/16 sdm PDF 313 std::is_constructible should refer to page 196, not 195. 1/ 5/17
1/ 8/15 bvw PDF 315 Bart Vandewoestyn ==> Bart Vandewoestyne 1/20/15
The following changes have been proposed, but have not yet been published:
DATE
REPORTED WHO PLATFORM LOCATION WHAT
-------- --- -------- -------- ----------------------------------------------------------
3/ 2/21 rtc PDF 4 In comment in last code example, "SomeFunc" ==> "someFunc".
6/17/20 dxr PDF 11 In Step 1 in the middle of the page, not only should the
reference part of expr's type be ignored (if it exists),
but the reference part of ParamType should also be ignored
(if it exists).
! 9/27/19 kst PDF 21 C++98 permits scalar types to be initialized using the
"type var = { value };" syntax, so the initialization of
x3 near the top of the page is valid in C++98. What C++11
did was extend this initialization syntax to non-scalar
types.
3/20/22 ayk PDF 57 In last code fragment on page, variable v is never used.
12/24/18 dxk PDF 77 Text on these pages refers to deleting instantiations or
79 declaring them private, but it's actually the template
specializations that are deleted or declared private, not
the template instantiations.
12/ 6/21 mxh PDF 86 In first para, "what the iterator points to" ==> "the data
the iterator points to", and in the para after the first
code example, "what an iterator points to" ==> "the data
an iterator points to".
4/ 9/19 fxr PDF 96 In middle para on page, the statement that functions in the
C++ Standard Library inherited from C are not declared
noexcept is true, but 17.6.5.12/3 of the C++11 and C++14
Standards makes clear that implementations should treat
them as if they were.
5/12/20 dyg PDF 163 In last para in Item, "only for references" ==> "only for
universal references".
3/12/19 gpk PDF 194 For consistency with the original code on page 180, remove
the "…" from both constructor bodies on this page.
5/10/19 dxk PDF 214 The second "Things to Remember" bullet should include
declaration-only integral constexpr data members.
12/17/19 jxr PDF 219 In sentence above last code display, there's an extra space
between "container" and "of".
9/8/20 dxt PDF 288 Lines 6-7 of the first para say that newName is copy-constructed
into the names vector, but it'd actually be move-constructed.
2/20/19 dxk PDF 314 In first column, add page 294 to entry for
std::vector::emplace_back.
What follows are interesting comments about the material in Effective Modern C++, but I don't expect to be able to modify the book to take these comments into account until (if ever) I write a second edition.
DATE
REPORTED WHO PLATFORM LOCATION WHAT
-------- --- -------- -------- --------------------------------------------------------------------
1/21/15 sdm PDF All The leading within paragraphs is sometimes inconsistent. This is
because the code font is slightly taller than the non-code font, so
lines containing text in code font get a bit more leading than those
without. Reducing the size of the code font 5% fixes that problem,
but it leads to a surprisingly large number of revised page breaks
throughout the book, so we've decided to live with the inconsistent
leading.
1/12/15 sdm Kindle All The book's TOC is at the end of the book, rather than at the
beginning. This is O'Reilly policy for digital books, because
E-readers have their own intrabook navigation systems.
11/18/14 sdm Kindle All On some platforms, spacing above or below code displays is excessive,
(e.g., on Kindle app on iPad, there's too much space above code
displays, whereas on Kindle for PC, there's too much space after
code displays). I'm told "there are known inconsistencies within the
Kindle device/app family, and there is no way to account for each
variation." Not that that has prevented us from trying.
11/21/14 bxm Kindle All Search functionality is...counterintuitive. Examples:
Kindle Actual
Text to Search For Hits Occurrences
------------------ ------ -----------
std::array 0 27
std:: array 25 0
std :: array 0 0
#include <string> 0 5
#include < string> 0 0
#include < string > 5 0
= delete 2 12
= delete; 10 10
If you're aware of a good description of how Kindle search
works, please let me know. I'd like to link to it.
1/13/15 sdm Kindle All The book's digital TOC lacks entries for individual Items, but this
on PC is apparently a limitation of Kindle on PC. On other Kindle platforms,
I'm told that the digital TOC does link to individual Items.
3/29/15 scp Kindle All Emphasized words (presumably italicized) are invisible. I'm told this
on HTC is a problem with the Kindle app when using the default font
One (M8) (Caecelia). Choosing a different font should resolve the issue.
! 10/ 5/14 txn All Item 1 Case 1 applies only when ParamType is a non-universal reference.
It doesn't apply when ParamType is a pointer type, because
parameters of pointer type are treated like by-value parameters.
(The pointer is passed by value.) So the three cases are really:
Case 1: ParamType is a non-universal reference.
Case 2: ParamType is a universal reference.
Case 3: ParamType is a non-reference.
This is a simpler way of classifying template type deduction
behavior: two major cases (reference parameters and non-reference
parameters) and two sub-cases for one of the major cases (universal
reference parameters and non-universal reference parameters).
4/29/21 dxw PDF 11 It could make things clearer if (1) an example were given where expr is a
reference, but not a reference-to-const, and (2) the variable name for
the reference-to-const were rcx instead of rx.
12/18/14 csc PDF 24 In the final line of the code example, if v were a const vector,
decltype(v[0]) would be const int&, because std::vector::operator[]
is overloaded on const, and the const version has a
reference-to-const return type.
7/ 5/17 rxz PDF 27 Technically, parameters bind to arguments, but in some places in the
87 book (as noted by the page numbers at left), I refer to arguments as
166 binding to parameters (or, in some cases, as expressions as binding to
192 variables). I believe the current wordings are pretty clear, and I'm
concerned that trying to rewrite the explanations to be technically
correct might make things worse, so I'm leaving as is the places
where I refer to things binding to names instead of names binding to
things.
3/17/15 fdb PDF 27-28 Except for std::vector<bool>, container<T>::operator[] for all
standard library containers returns T&, which is an lvalue. Given an
rvalue container rc, then, indexing into it (e.g., rc[i]) yields an
lvalue, even though rc itself is an rvalue. Because authAndAccess
with a decltype(auto) return type returns exactly what
container<T>::operator[] does, an implication is that in the code
defining s on page 27, s will be copy constructed, even though the
element initializing it is in an rvalue std::deque<string>.
authAndAccess could be revised to return rvalues for rvalue
container arguments, but the crux of the issue in this case is that
operator[] for rvalue containers yields an lvalue. Given C++11's
support for overloading on lvalue and rvalue objects (as described
in Item 12 of EMC++), it'd be interesting to know why operator[]
for an rvalue object doesn't return an rvalue reference (which, for
function return types, is an rvalue).
1/26/15 tyk All Item 3 The behavior of these two ways of declaring a function template may
not be the same:
template<typename T>
auto f()->decltype(expr) // #1 -- explicit return type
{
...
return expr;
}
template<typename T>
decltype(auto) f() // #2 -- deduced return type
{
...
return expr;
}
In #1, if expr is invalid, SFINAE will cause f to be removed from
the candidate overload set, but in #2, if expr is invalid, f
will be ill-formed, and compilation will fail. The same would be true
for an auto return type. This means that it's not always possible to
replace an explicit return type with a deduced return type.
(If the sentence with "SFINAE" makes no sense to you, you should
probably not be writing templates with auto or decltype(auto) return
types.)
6/19/16 axd All 38 The text suggests that it's always a good idea to initialize
variables, but there are cases where it makes sense to leave
variables uninitialized (e.g. as described here).
11/22/14 tyk All Item 6 A noteworthy difference between the explicity typed initializer
idiom (ETII)'s
auto var = static_cast<type>(expr);
and this auto-free alternative,
type var = expr;
is that explicit conversions are permitted in the ETII version, but
not in the auto-free one. However, the ETII version is equivalent to
these auto-free versions:
type var(expr);
type var{expr};
In effect, the ETII performs direct initialization, not copy
initialization, and as such is arguably less safe (due to support
for more conversions) than an auto-free variable declaration using
copy initialization syntax.
11/23/14 bbs PDF 46 A way to confirm that an expression (such as a function call)
returns the type you expect is to use static_assert. On page 45, for
example, the type of the object sum could be asserted to be Matrix:
static_assert(std::is_same<decltype(sum), Matrix>::value,
"sum isn't a Matrix!");
Typically, you want to strip reference and cv-qualifiers before
doing the comparison, so you can use std::decay (as on pages
190-191), but, as I note on page 191-192, std::decay will also turn
arrays and functions into pointers, and you may not want that. In
that case, you'll want to use something more like R. Martinho
Fernandes' Unqualified type trait.
4/23/15 nwp PDF 59 The fact that nullptr isn't a pointer type means that it can't be
passed to templates partially specialized for pointers:
template<typename T>
void f(T*); // partial specialization for pointers
f(nullptr); // error!
10/24/14 dxn PDF Item 9 Just as C++14 standardizes the convention of using a "_t" suffix
for nested types, C++17 is likely to standardize the convention of
using a "_v" suffix for values (i.e., a nested "value" member).
1/25/15 tyk All Item 9 Another advantage of alias templates over typedefs nested inside
templatized structs is that type deduction is applicable to
parameters of alias template types, but not to parameters of
typedef-inside-a-struct types:
template<typename T> // alias template from
using MyAllocList = std::list<T, MyAlloc<T>>; // Item 9 (PDF page 64)
template<typename T> // f1 is a function
void f1(MyAllocList<T>& list); // template
MyAllocList<Widget> lw; // also from Item 9
f1(lw); // fine, T in f1
// deduced as Widget
template<typename T> // struct with nested typedef
struct MyAllocList { // from Item 9 (PDF page 64)
typedef std::list<T, MyAlloc<T>> type;
};
template<typename T> // f2 is a function
void f2(typename MyAllocList<T>::type& list); // template
MyAllocList<Widget>::type lw; // also from Item 9
f2(lw); // error! can't
// deduce T in f2
11/23/14 bbs PDF 71-73 In C++14, std::tuples can be indexed by the type of a field instead
of its index, as long as the type is unique. That has only limited
applicability to the example in the book, because the first and
second fields are of the same type (std::string), but it's still
worth knowing about.
6/19/16 axd All 71-73 Another approach to creating enums to identify std::tuple fields is
to nest an anonymous enum inside a struct, e.g.:
struct UserInfoFields
{
enum { name, email, reputation };
};
auto val = std::get<UserInfoFields::name>(uInfo);
2/12/15 hxh All Item 11 Move operations should probably never be deleted. If you want
neither moving nor copying to be available, deleting the copy
operations suffices. (That will prevent generation of the move
operations.) If you want only copying to be supported, declaring
the copy operations (possibly via "=default") will again prevent
the move operations from being generated.
If you try to permit copying but disable moving by declaring the
copy operations but deleting the move operations, you will prevent
the copying of rvalues, because the deleted move operations will
be a better match during overload resolution than the "fallback"
copy operations.
3/29/15 dxg All Item 11 dxg writes: "I had a class with default constructor declared as
private without implementation. I decided to do a simple refactor
and made the function public and deleted. After that clang found an
unused field in the class! While the function had a declaration
compiler had no any information about its body, so it can't prove
that the field has been unsued (the constructor could be defined
anywhere with the usage of the field)."
1/25/15 tyk All 83-85 One application of reference-qualified member functions is to prevent
modification of rvalues (e.g., objects returned from
functions). Such modification is not permitted for rvalues of
built-in types, so this would be a way for user-defined types to
mimic that behavior.
The approach is to restrict use of modifying member functions to
lvalues by "&"-qualifying them, e.g.:
class Widget {
public:
Widget& operator=(const Widget&) &; // copy-assign only lvalues
Widget& operator=(Widget&&) &; // move-assign only lvalues
void setName(const std::string&) &; // setter is only for lvalues
};
12/18/14 ahp PDF 88-89 The Standard requires that std::cbegin be implemented by calling
1/26/15 tyk std::begin (as shown in the book), but in C++14, the code could be
made a smidge more generic by using the following implementation,
which, unlike the code in the book, would work with container-like
objects supporting non-member begin instead of member begin:
template <class C>
decltype(auto) cbegin(const C& container)
{
using std::begin;
return begin(container);
}
Achieving the same thing in C++11 is trickier, because we have to
make std::begin visible during evaluation of the return type
declaration without making it visible outside the cbegin function
we're declaring. The following works:
namespace cbegin_access_impl { // helper namespace
using std::begin;
template<class C>
auto cbegin(const C& container)->decltype(begin(container))
{
return begin(container);
}
}
template<class C>
auto cbegin(const C& container)->decltype(cbegin_access_impl::begin(container))
{
return cbegin_access_impl::begin(container);
}
1/24/15 sdm PDF 92 In the third sentence of the fourth paragraph, the word "sporting"
is correct; it's not a typo for "supporting." To "sport" something
can mean to wear it as an adornment, so in the sentence in the book,
I'm talking about functions that offer (i.e., that "sport") the
strong exception safety guarantee.
12/19/14 ahp All Item 16 For cached values that are constant, use of a std::once_flag and
std::call_once is probably preferable to use of a mutex or a
std::atomic. However, if a cached value can change over time (e.g.,
in the examples in Item 16, if the coefficients of a Polynomial can
be changed at runtime (thus changing its roots) or if the value
returned by Widget::magicValue changes each day at midnight), the
approaches shown in the Item are likely to be more appropriate.
11/17/15 jxl All 105-6 Though introduction of the std::mutex causes Polynomial and Point to
2/ 9/17 abt become uncopyable and unmovable, copyability and movability can be
restored by defining the copy and move operations manually. abt
writes that one way to approach the matter would be to "create
a new mutex for the object being created and then copy everything
else. Likewise, custom copy/move assignment operators would be
required and would copy/move all items except for the mutex."
11/17/15 jxl PDF 106-7 If magicValue is called often enough and the cost of mutex acquisition
is high enough compared to the cost of using std::atomics, it could
be an overall performance improvement to use std::atomics for
cacheValid and cachedValue, even though cachedValue might be computed
more than once. That's because the cost savings on each access could
more than compensate for the excess costs associated with multiple
magicValue computations.
11/17/15 jxl PDF 108 If more than one variable or memory location requires synchronization,
but all can be packed into the amount of memory that can be manipulated
truly atomically by the underlying hardware (typically one or two
words), an alternative to use of a std::mutex is a std::atomic<PackedType>,
where PackedType is a user-defined type containing the variables to be
manipulated as a unit.
1/11/16 sdm All Item 17 Compiler-generated special functions are implicitly noexcept unless
their compiler-generated implementations call non-noexcept
functions. Destructors and move operations rarely call functions
that can throw, so implicitly-generated destructors and move
operations are typically noexcept. (As noted in Item 14, even
user-declared destructors are usually implicitly noexcept.)
1/18/15 bbs PDF 117 Another disadvantage of raw pointers is that deleting incomplete
types through them, while often eliciting a warning,
nevertheless compiles. Trying to do the same thing through a smart
pointer is an error, and the code
typically fails to compile.
8/31/16 ryk PDF 117 Two more disadvantages of raw pointers:
- If they're not explicitly initialized, their initial value may
not be defined. std::unique_ptr and std::shared_ptr are
initialized to null by default.
- As data members, they typically require that the containing
class implement the special member functions. With smart pointer
data members, this is generally unnecessary.
12/13/14 rxm All Item 18 The makeInvestment interface is unrealistic, because it implies that
all derived object types can be created from the same types of
arguments. This is especially apparent in the sample implementation
code, where are arguments are perfect-forwarded to all derived class
constructors.
12/15/14 sdm PDF 122 The encapsulation offered by the "auto"-returning version of
makeInvestment is diluted by the fact that in order to call it, its
body must be visible. In practice, this means that functions with
deduced return types must often be defined in header files.
On the plus side, the "auto"-returning version of makeInvestment
prevents the name "delInvmt" from being visible outside the
function.
1/28/15 tyk PDF 122-123 A drawback of giving makeInvestment an "auto" return type is that
it complicates things for callers who need to know that type (e.g.,
to declare a container holding objects returned from
makeInvestment). tyk argues that there's no point in trying to hide
a factory function's return type.
1/18/15 bbs PDF 124 A situation where you might choose to use std::unique_ptr<T[]>
instead of a standard container is when the size of the array isn't
known during compilation (thus ruling out std::array) and where you
want to minimize memory usage. The footprint of std::unique_ptr<T[]>
is slightly smaller than that of a std::vector, std::deque, or
std::string.
12/20/14 tyk All Item 21 Make functions can't be used when the constructor to be called isn't
6/23/15 whb public. (Declaring the make functions friends may not work, because
make functions may internally call other functions that actually
perform the constructor invocation.) An example of this is in Item
19 (on page 132 of the PDF version of the book), where Widget
declares a static factory function instead of using a make function.
For workarounds, see this StackOverflow discussion.
3/23/15 etc All Item 21 Make functions can't be used to create smart pointers to abstract
base classes, e.g., that are initialized by cloning functions. For
example:
class Base {
public:
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const override;
};
Derived d;
auto pb1 = std::make_shared<Base>(d.clone()); // error!
auto pb2 = std::shared_ptr<Base>(d.clone()); // fine
5/ 8/18 hxp All 140-141 In C++17, argument evaluation order rules have been revised such
that no resource leak can occur.
1/29/15 tyk PDF 152 In second para, I say that the compiler-generated move operations do
"exactly what's desired: perform a move on the underlying
std::unique_ptr". Such moves are shallow, but for a class with value
semantics like Widget, deep move operations would be at least as
reasonable, and they'd avoid the need to handle null pImpl pointers
in Widget member functions. However, they'd also require allocating
and initializing a Widget::Impl object for moved-from objects, even
if the next operation to be performed on the Widget was destruction.
1/19/15 bbs All Item 22 The Pimpl idiom reduces build times, but the indirection introduced
by the pPimpl pointer may increase runtime, and the pointer itself
increases total memory usage.
3/10/16 mtl PDF 154 The shown Widget::operator= works correctly if a Widget is
assigned to itself, but such assignment would incur the cost of
copying an Impl object, and that could be expensive. If that cost is
sufficiently high, or if self-assignment is expected to be common,
it could be worthwhile to add the customary self-assignment check to
the top of operator=:
Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs) return *this;
... // implementation as in book
}
However, this kind of check has costs of its own. The function is a
little longer, and a new branch is added.
2/11/15 sdm PDF 171 In last paragraph, I say that the number of overloads grows
geometrically instead of exponentially, because, per
the Wikipedia page, "geometric" is appropriate for discrete domains,
while "exponential" applies to continuous domains.
5/24/20 dyg PDF 175-6 The fact that local objects eligible for the RVO and not elided
must be treated as rvalues is the reason why functions returning
move-only types may return local variables of those types.
12/19/14 ahp All Item 26 The fact that template functions may outcompete non-template
functions with the same name (i.e., overloads) is not unique to
templates taking universal references or even to C++11. The same
thing can happen in C++98 with a template taking a const T&
parameter, because such a parameter can bind to all types and can
accept both lvalues and rvalues. In my (Scott's) experience,
however, this rarely leads to trouble in practice, probably because
there are no move semantics in C++98.
3/ 1/18 ext PDF 187-8 An alternative to having logAndAddImpl at the top of page 188 call
logAndAdd is to have it call directly to the version of
logAndAddImpl taking a non-integral type, i.e.,
void logAndAddImpl(int idx, std::true_type)
{
logAndAddImpl(nameFromIdx(idx), std::false_type());
}
This approach has the advantages that (1) it avoids the call to
logAndAdd that merely forwards to logAndAddImpl and (2) it may
simplify the declaration order among the logAndAdd template and the
logAndAddImpl template specializations.
1/21/15 sdm All 195-6 Several people have suggested moving std::is_constructible from a
static_assert inside the Person constructor to the
std::enable_if/std::enable_if_t outside it. This would change the
behavior of the code. The enable_if controls whether the constructor
should be considered during overload resolution. The static_assert
controls whether the function, once it’s been chosen by overload
resolution, has received a valid argument type. If
std::is_constructible were part of the enable_if clause and a caller
passed an argument of an invalid type, the error message would say
something like "No function found for an argument of type [type of
argument]." With std::is_constructible inside the static_assert, the
error message would say "Parameter n can't be used to construct a
std::string."
2/ 3/16 shc PDF 196 To get the message specified in the static_assert to appear before
the errors resulting from invalid template instantiation, the
static_assert can be moved into a lambda whose invocation precedes
the normal initializer in a comma expression in the member
initializer list:
class Person {
public:
template< // as before
typename T,
typename = std::enable_if_t<
!std::is_base_of<Person, std::decay_t<T>>::value
&&
!std::is_integral<std::remove_reference_t<T>>::value
>
>
explicit Person(T&& n)
: name((
[]{ static_assert(std::is_constructible<std::string, T>::value,
"Parameter n can't be used to construct a std::string"); }(),
std::forward<T>(n)
))
{
… // the usual ctor work goes here
}
… // remainder of Person class (as before)
};
The code is a bit tricky, because you have to surround the comma
expression with parentheses in order to avoid an incorrect parse
(as explained here).
Note that it's not possible to use the static_assert in the comma
expression directly (i.e., to omit the lambda), because only
expressions can be used in a member initializer, and static_assert
is a declaration, not an expression.
12/31/14 axk All Item 28 If a template taking a parameter of type cv T& (where cv is
2/ 1/15 tyk const or volatile or both) is explicitly instantiated with a
reference type, cv refers to the reference in the explicitly-
specified reference type and is ignored. Reference-collapsing then
applies as usual:
template<typename T>
void func(const T& param);
int x;
func<int&>(x) // inside func, param's type is
// int&, not const int&
For details, consult this StackOverflow question.
The phenomenon of cv qualifiers disappearing when references to
references arise is not limited to function template parameters. In
general, if references to references arise during compilation (e.g.,
in parameter type construction, typedef construction, or type
creation via decltyp expressions), any cv-qualifiers that apply to
the referred-to type are discarded before reference collapsing
occurs. Here's a modified example from the end of Item 28 (on
page 202 of the printed and PDF versions of the book):
template<typename T>
class Widget {
public:
typedef const T&& RvalueRefToConstT; // compared to example
… // in book, const has
}; // been added
Widget<int&> w; // w::RvalueRefToConstT is int&
3/12/19 gpk All 209 Another workaround for the inability to perfect forward braced
initializers is to overload the forwarding function such that
a new overload takes a std::initializer_list:
template<typename T> // as in book
void fwd(T&& param) { … }
template<typename T> // new overload
void fwd(std::initializer_list<T> ilist)
{
f(ilist);
}
fwd( {1,2,3} ); // fine, calls new overload
gpk notes that this approach seems to violate Item 26 ("Avoid
overloading on universal references"), but in this case, the
overly greedy behavior of universal reference parameters isn't a problem.
In fact, when it comes to braced initializer arguments, the problem in
some sense that they're not greedy enough.
12/19/14 ahp All Item 30 A workaround for the inability to perfect-forward const/constexpr
data members and bitfields is to use the member to create a
temporary expression with the same value. For example, instead of
"foo(my_static_const)", pass "foo(+my_static_const)", or instead of
"foo(my->bit_field)", pass "foo(my->bit_field+0)". This must be
applied at each call site and may change the type of the expression
(chars and shorts will typically be promoted to ints, for example),
but it may be acceptable in some contexts.
If a static member is in a library that is header-only, this
approach keeps it header-only, and if the class is part of a library
not under the programmer's control, this solution avoids providing a
definition for a symbol that the user didn't declare.
8/10/15 nwp PDF 216 Because each lambda gives rise to a unique closure class, two
textually identical lambdas create different types. This can
lead to code duplication if the closure type's operator() isn't
inlined. The problem can be avoided by storing the lambda's closure
in an auto variable and using that variable in all locations
where the lambda would otherwise be employed:
f1([x, y, z]{ /* complex code */ }); // each call creates a
f2([x, y, z]{ /* same code as above */ }); // new closure class with
// a new operator()
// function
auto func = [x, y, z]{ /* same code as above*/ };
f1(func); // both calls use the
f2(func); // same closure class's
// operator() function
12/19/14 ahp PDF 223 Just as "[=]" could mislead readers into thinking that a lambda was
self-contained, "[]" could have the same effect; a reader
might think that "no capture" implies "self-contained." The behavior
of both "[=]" and "[]" lambdas can be dependent on objects with
static storage duration.
6/15/14 tyk All Item 34 Lambdas can be much more verbose than std::bind. This is especially
true for a bound "pass through" function, i.e., a bound function
whose arguments are perfect forwarded and whose return value is
decltype(auto)'d to the caller (i.e., where "perfect returning" is
used). For example, given
class Widget {
public:
const std::vector<std::string>& update(std::string newVal);
...
};
and a Widget w we know we want to invoke update on, compare the
following equivalent ways to express the binding of w and update:
// assume "using namespace std::placeholders;" has already been done
auto boundWithBind = std::bind(&Widget::update, w, _1);
auto boundWithLambda =
[w](auto&& newVal)->decltype(auto)
{ return w.update(std::forward<decltype(newVal)>(newVal)); };
2/ 8/15 tyk All Item 35 "Task-based programming" is a design approach based on (1) defining
units of work ("tasks") to be executed, (2) specifying the data and
timing dependencies among the tasks, and (3) relying on the runtime
system to execute the tasks such that the dependencies are
respected. std::async is the best tool in the C++14 Standard
Library for such programming, but it doesn't fully enable task-based
programming, as this blog post explains.
6/19/16 axd All 244 The book notes that task-based programming significantly reduces
the likelihood of an out-of-threads exception, but that doesn't
change the validity of page 243's statement that well-written
software must deal with such exceptions. Task-based programming
therefore has no fundamental advantage over thread-based
programming as regards the need for software to address out-of-thread
exceptions.
12/18/17 pxj All 244 Rigorously speaking, the std::launch::async launch policy doesn't
246 guarantee that std::async will run the passed-in function on a
different thread, it merely guarantees that program execution will
proceed as if the passed-in function ran on a different thread.
This flexibility might permit implementations to, for example, run the
passed-in function on an existing-but-not-currently-used thread in a
thread pool, or to optimize out execution of the passed-in function
entirely (e.g., if they could establish that eliminating execution of
the function would have no effect on the observable behavior of the
program).
2/ 8/15 tyk PDF 251-256 The program will be terminated if an exception arises in the lambda
expression inside doWork, i.e., if the filtering function or
std::vector::push_back throws. This can be fixed by wrapping the
lambda in a std::packaged_task, but if control in doWork never
reaches performComputation (e.g., due to conditionsAreSatisfied
returning false or to an exception arising while t's priority is
being set), the use of ThreadRAII::DtorAction::join on page 256
would cause doWork to block waiting for the lambda to finish, even
though its result (the data in goodVals) would never be used.
Having the lambda move-capture filter and return goodVals would make
it possible to change the ThreadRAII DtorAction to
ThreadRAII::detach, thus eliminating the need to block. Rewriting
doWork to incorporate these ideas yields:
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::packaged_task<std::vector<int>()>
filterTask([filter=std::move(filter), maxVal]
{
std::vector<int> goodVals;
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
return goodVals;
});
auto goodValsFuture = filterTask.get_future();
ThreadRAII t(std::thread(std::move(filterTask)),
ThreadRAII::DtorAction::detach);
auto tnh = t.get().native_handle();
…
if (conditionsAreSatisfied()) {
auto goodVals = goodValsFuture.get();
performComputation(goodVals);
return true;
}
return false;
}
A noteworthy aspect of this implementation is that if an exception
occurs in the lambda, it will be propagated to doWork only if
conditionsAreSatisfied returns true. If conditionsAreSatisfied
returns false, the exception will be ignored.
10/27/15 kzh PDF 254 std::thread::join and std::thread::detach can emit exceptions, and
that means that ThreadRAII's destructor can throw. If this happens
during stack unwinding due to another exception, program execution
either terminates or yields undefined behavior.
My advice on how to approach this problem is described in Item 8
of Effective C++, Third Edition: add a function to ThreadRAII
that clients can call if they need to be able to handle exceptions
that may arise when the destructor action is performed.
1/19/15 bbs All Item 38 The fact that the final future referring to a shared state created
via a call to std::async and where the launch policy is
std::launch::async blocks until the asynchronously running task
completes means that "fire and forget" std::async calls that ignore
std::async's return value may block until the asynchronously running
task finishes. That is, such calls may run synchronously. That's
because the std::future returned from std::async will be destroyed
at the end of the statement calling std::async, so if the launch
policy is std::launch::async, that std::future's destructor will
block until the invoked task finishes running. (The Standardization
Committee considered changing this behavior, but they ultimately
decided to keep it in the interest of backwards compatibility.)
2/ 7/15 tyk All Item 40 A secondary use for volatile is to prevent compilers from optimizing
away code that has no effect on observable program behavior. If you
try to time an empty loop to calcuate the loop overhead (e.g., try
to time "for (int i = 0; i < 10'000'000; ++i);"), compilers
will typically optimize the loop away and report that no time was
spent there. To prevent the optimization, the loop variable can be
declared volatile: "for (volatile int i = 0; i < 10'000'000; ++i);".
1/30/15 tyk All Item 41 Another behavioral difference between (1) overloading for lvalues
and rvalues and (2) a template taking a universal reference (uref)
is that the lvalue overload declares its parameter const, while the
uref approach doesn't. This means that functions invoked on the
lvalue overload's parameter will always be the const versions, while
functions invoked on the uref version's parameter will be the const
versions only if the argument passed in is const. In other words,
non-const lvalue arguments may yield different behavior in the
overloading design vis-a-vis the uref design.
1/30/15 tyk All Item 41 A universal reference parameter can bind to virtually any kind of
argument. (Exceptions are covered in Item 30.) An implication is that
metaprogramming techniques based on testing the validity of function
calls with certain kinds of arguments are rendered largely useless,
because virtually all calls to functions taking universal references
are considered valid. That's not the case with functions taking
parameters of specific types, because only argument types compatible
with the parameter types yield valid calls. This difference can be a
reason to overload for lvalues and rvalues of specific types instead
of writing a template taking universal references.
Who's who:
txn = Tyson Nottingham tjb = Tyler Brock dxn = Daniel Nielsen akb = Agustín K-ballo Bergé kxv = Kostas Vlahavas mxs = Minkoo Seo txk = Takatoshi Kondo axk = Andrey Khalyavin lxs = Lewis Stiller fxf = Fabrice Ferino dxj = Dainis Jonitis jxz = Jay Zipnick tyk = Tomasz Kamiński oxb = Oliver Bruns kxa = Ken Aarts bvw = Bart Vandewoestyne rxb = Ryan Brown bxy = Benjamin Yates daa = Daniel Alonso Alemany bxm = Bernhard Merkle bxr = Barry Revzin pxv = Petr Valasek rxm = Robert McCabe mxw = Marcel Wid rxk = Rex Kerr mxm = Miroslaw Michalski kxh = Kjell Hedstrom mjs = Matthias J. Sax csc = Curtis S. Cooper mam = Mark A. McLaughlin bbs = Bartek Szurgot vhg = Vlad Gheorghiu ahp = Adam Peterson mxk = Mitsuru Kariya hxh = Howard Hinnant gxs = Grebёnkin Sergey sxt = Semen Trygubenko fdb = Ferdinand deBoevere lxz = Leor Zolman dxg = Denis Gladkiy scp = Siu Ching Pong lxp = Lucas Panian etc = Eitan Frachtenberg nwp = Nathan W. Panike pxz = Piotr Zygielo dgw = Don Goodman-Wilson whb = Will Bickford mys = Marek Scholle kyh = Kisung Han kzh = Kalle Huttunen rxh = Rein Halbersma bxc = Ben Craig jxl = Johannes Laire nxj = Nicolai Josuttis shc = 蘇浩禎 Su Hao-Chen jsb = Jesper Storm Bache jxm = József Mihalicza mtl = Matthew Limber gxk = Gerhard Kreuzer gxp = Gennaro Prota axd = Alex Dumov mtb = Mitch T. Besser ryk = Robin Kuzmin txb = Tim Buchowski hxb = Harri Berglund cxl = Calum Laing djm = Declan Moran abt - Allen Taylor sxa = Stephane Aubry tgm = Tomasz Grzegorz Markiewicz rxz = Ryan Zischke pxj = Péter Joó ext = Evgeny Tarasov hxp = Helge Penne dxk = Daniel Khoshnoudirad gpk = Greg Klein fxr = Fraser Ross kst = Keith S. Thompson dyg = Dmitriy Gruzman dxr = Dan Raviv dxt = David Tzur jxr = Julio Merino rtc = Roberto Turrado Camblor dxw = Wang Dianchao 王殿超 mxh = Marja Hölttä ayk = Andrew Kay
From here you may wish to visit the Amazon Page for Effective Modern C++.

