Before I summarize the Items I currently plan to include in
Effective C++11 ("EC++11"), I'd like to explain a little about how I come up with the guidelines in my books.
It'd be sweet to imagine that I develop all the guidelines myself. If you're very technical about it, I do. I come up with all my own Item wordings, and I write all my own Item justifications. But the
ideas behind the guidelines have generally already fought their way to acceptance in the C++ community. It's this acceptance that gives me confidence that the advice in the guidelines is both accurate and useful. My
Effective books strive to summarize practices that are
known to be effective, not to introduce new practices I
hope will be.
In the case of C++11, the Standard is only about 18 months old, and some parts of it are yet to be implemented by major compilers. The experience with C++11 that is necessary to identify useful advice is therefore limited. But even the features that have not seen widespread use have been discussed and debated for years, and sometimes that gives rise to "observations" about those features that seem likely to be important. Consider, for example, what happens in a destructor for a
std::future. If the future came from
std::async, the future's destructor blocks until the asynchronously running thread completes. If the future did not come from
std::async, the future's destructor doesn't block. That's already interesting, but, in my mind, it becomes even more interesting when combined with the observation that the destructor for a "joinable"
std::thread calls the terminate function. So we have three different behaviors for a single high-level concept: destruction of the object "responsible" for another thread.
I think this divergent behavior is likely to trip people up, and that gets it on my candidate list for EC++11. But what is the Item? What is the advice? I could go with the lame-o "Understand destructor behavior in
std::future and
std::thread," but that's just cloaking an excuse to describe language rules in guideline form. I'm not above doing that when I can't think of anything better, but I prefer to find a way to offer
advice that could be checked by a tool or in a code review. "Understand" rules fail that test.
(As an aside, the behavior of the destructors for
std::futures produced by
std::async is controversial and may change in the next revision of C++ (currently forecast to be in 2014). Some implementations already deviate from the behavior dictated by the Standard. That muddies the water yet further, but that's a problem for me and my book, not this blog post.)
You can think of "observations" about C++11 as proto-Items. They're not yet in guideline form, but they seem important enough to justify trying to find a way to work them into the book. Whether they'll make the book, and, if so, whether they'll do it as a standalone Item or as a side-discussion in another Item, I don't yet know.
At last year's
C++ and Beyond, I gave a talk entitled "
Initial Thoughts on Effective C++11." It had my usual guideline format. I also gave a talk on "
Secrets of the C++11 Threading API," which consisted of observations about C++11's threading support.The material in those talks, combined with the feedback I got from giving them and mixed in with my experience explaining the idea of
universal references, ultimately yielded the initial list of candiate Items for EC++11. The current snapshot of my vision for
Effective C++11 is:
Moving from C++98 to C++11
- Prefer
auto to explicit type declarations.
- Remember that
auto + { expr } ⇒ std::initializer_list.
- Prefer
nullptr to 0 and NULL.
- Prefer
enum classes to enums.
- Prefer alias templates to typedefs.
- Declare overriding functions
override.
- Distinguish
() and {} when creating objects.
- Prefer emplacement to insertion.
- Declare functions
noexcept whenever possible.
- Make
const member functions thread-safe.
- Avoid
std::enable_if in function signatures.
- Handle iterators where copying means moving.
Rvalue References, Move Semantics, and Perfect Forwarding
- Distinguish universal references from rvalue references.
- Avoid overloading on universal references.
- Pass and return rvalue references via
std::move, universal references via std::forward.
- Assume that move operations are not present, not cheap, and not used.
- Be aware of perfect forwarding failure cases.
- Understand reference collapsing.
Secrets of the C++11 Threading API
- Thread construction may throw.
- Destroying a joinable thread calls terminate.
- Arguments passed to
std::thread, std::async, and std::call_once are unconditionally copied.
std::async is really two different functions with somewhat different APIs.
- Futures from
std::async are special.
void futures can be used for interthread communication.
- To poll a future, use
wait_for with a zero timeout.
- Native handles let you go beyond the C++11 API.
- Clock adjustments affect
_until functions.
Lambda Expressions
- Prefer lambdas to
std::bind.
- Prefer lambdas to variadic arguments for threading functions
- Beware default captures in member functions.
Smart Pointers
- Use
std::make_shared whenever possible.
- Prefer pass-by-ref-to-const to pass-by-value for
std::shared_ptrs.
Miscellaneous
- Pass by value when you’ll copy your parameter.
- Keep abreast of standardization developments.
The number of Items on this list, the wording they have, the order in which they will occur, and whether they will ultimately be present are not just subject to change, they
will change. This is especially true for the material pertaining to the threading API, because those "Items" are still just observations. But that's what my draft TOC looks like right now.
I also have a set of candidate guidelines that I currently feel are less important and hence less likely to make the book. This list will also change over time, but for your voyeuristic pleasure, this is what it contains:
- Prefer non-member
begin/end to member versions.
- Declare
std::thread and std::future members last.
- For copyable types, view move as an optimization of copy.
- Understand
decltype.
- Use
std::compare_exchange_weak in loops.
If you have comments regarding any of the potential guidelines above, or if you have suggestions about what's not above but you believe should be, feel free to let me know, either as comments on the blog or via email: smeyers@aristeia.com.
"How much of this book have you written?," you may be wondering. None. Not a word. Zero percent. I haven't started writing, in part because I've allowed myself to get distracted by things like, um, creating blog entries...
But wording is everything. If you change the question to "How much of the work required to write this book have you already done?," the answer changes. When I
announced the existence of my annotated training materials for C++11, I explained that my approach to writing a book consists of three steps:
- Master the material.
- Figure out what "story" I want to tell, i.e., what to cover, what to
omit, what order to cover things in, what examples to use, etc.
- Write it up.
I went on to break step 2 down as follows:
- 2a: Come up with a story that I think will work, i.e., that will effectively convey the technical information.
- 2b: Develop a training course corresponding to that story.
- 2c: Deliver the training course to professional developers and see
how well the story works. In places where it doesn't work as well as it
should, return to step 2a and iterate until everything is satisfactory.
For EC++11, I've pretty much completed steps 1 and 2, so from that perspective, I'm two-thirds done with the book :-) In theory, I know what I need to say. I just need to write it down. In reality, that understates the difficulty of the work that's still to be done, but to some degree, what remains is an IO-bound operation.
I would hope it goes without saying that you can't have too many iterations of steps 2a-2b-2c. Every time I present the material, I learn things about how I can do a better job. Sometimes it's about something I've said that was confusing or unnecessary. Sometimes it's about something I didn't say, but should have. Sometimes it's about a relationship between what I'm discussing and another topic that seemed completely independent to me, but didn't to the people I was working with. To that end, I'll be making several presentations of a new training course called
Effective C++11 Programming several times this year. The first is slated to take place in June. I'll post details soon.
For the next few months, I'm doing my best to keep my calendar free. I need time to write
Effective C++11. You know how slow IO-bound operations can be.
Scott