Monday, March 31, 2014

Declare functions noexcept whenever possible?

In the comments following my last post, there was some controversy regarding the wording of my advice on noexcept functions. My advice is "Declare functions noexcept whenever possible." Some people appear to be concerned that this could be misconstrued as advocating noexcept even when it makes no sense, but I think the advice is a reasonable conclusion to the Item that supports it. I posted a draft version of that Item in early February, but I've revised the draft since then, and I'm making the current draft available now:

The current draft of "Declare functions noexcept whenever possible."

I'd be interested to hear what you think of (1) the wording of the Item title and (2) the contents of the Item itself. I'd also be interested to know what you think about these questions:
  • Should I say that constexpr functions are normally good candidates for noexcept?
  • Do lambdas get special consideration, or should they, too be declared noexcept whenever possible. If they're special, why?
  • Do inline functions get special consideration, or should they also be declared noexcept whenever possible? If they're special, how so?
Please let me know what you think.

Thanks,

Scott

Tuesday, March 18, 2014

Book Report: New Title, New TOC, New Sample Item

The purpose of this post is to update you on the status of the book I'm working on. It includes a new working title, a revised table of contents and a new draft sample Item.

Thanks to comments on my blog post asking for suggestions about how to name the book, I've changed the working title to Effective Modern C++, and I've accustomed myself to distinguishing the abbreviation EMC++ (the current book) from MEC++ (More Effective C++). Sharp-eyed readers may have noticed that the new title has already appeared on a talk I'll be giving in June at the Norwegian Developers Conference.

I recently finished the 32nd Item for the book, thus giving me drafts of five full chapters. The math still shows that about 40 Items will fit in the book's allotted 300 pages, so yesterday I took a hatchet to the prospective table of contents and chopped the number of Items down from 51 to 41. (Why 41? Because I have a feeling that one of the Items I've written will eventually get jettisoned as not being important enough to make the final cut.)  Here's the current draft TOC. The chapters I've written are 1-4 and 6 (i.e., chapters 5 and 7 remain on my todo list).

Chapter 1 Deducing Types
  Item 1: Understand template type deduction.
  Item 2: Understand decltype.
  Item 3: Know how to view deduced types.

Chapter 2 auto
  Item 4: Prefer auto to explicit type declarations.
  Item 5: Remember that in variable declarations, auto + { expr }
          yields a std::initializer_list.
  Item 6: Be aware of the typed initializer idiom.

Chapter 3 From C++98 to C++11 and C++14
  Item 7: Distinguish () and {} when creating objects.
  Item 8: Prefer nullptr to 0 and NULL.
  Item 9: Prefer alias declarations to typedefs.
  Item 10: Prefer scoped enums to unscoped enums.
  Item 11: Prefer deleted functions to private undefined ones.
  Item 12: Declare overriding functions override.
  Item 13: Prefer const_iterators to iterators.
  Item 14: Use constexpr whenever possible.
  Item 15: Make const member functions thread-safe.
  Item 16: Declare functions noexcept whenever possible.
  Item 17: Consider pass by value for cheap-to-move parameters that
           are always copied.
  Item 18: Consider emplacement instead of insertion.
  Item 19: Understand special member function generation.

Chapter 4 Smart Pointers
  Item 20: Use std::unique_ptr for exclusive-ownership resource
           management.
  Item 21: Use std::shared_ptr for shared-ownership resource
           management.
  Item 22: Use std::weak_ptr for std::shared_ptr-like pointers that can
           dangle.
  Item 23: Prefer std::make_unique and std::make_shared to direct use
           of new.
  Item 24: When using the Pimpl Idiom, define special member
           functions in the implementation file.

Chapter 5 Lambda Expression
  Item 25: Avoid default capture modes.
  Item 26: Keep closures small.
  Item 27: Prefer lambdas to std::bind.

Chapter 6 Rvalue References, Move Semantics, and Perfect Forwarding
  Item 28: Understand std::move and std::forward.
  Item 29: Distinguish universal references from rvalue references.
  Item 30: Pass and return rvalue references via std::move, universal
           references via std::forward.
  Item 31: Avoid overloading on universal references.
  Item 32: Understand alternatives to overloading on universal
           references.
  Item 33: Understand reference collapsing.
  Item 34: Assume that move operations are not present, not cheap,
           and not used.
  Item 35: Familiarize yourself with perfect forwarding failure cases.

Chapter 7 The Threading API
  Item 36: Make std::threads unjoinable on all paths.
  Item 37: Specify std::launch::async if asynchronicity is essential.
  Item 38: Be aware of varying thread handle destructor behavior
  Item 39: Consider void futures for one-shot event communcation.
  Item 40: Reserve std::atomic for concurrency, volatile for memory-mapped
           I/O.
  Item 41: Employ sequential consistency if at all possible.

You may want to compare this to the initial preliminary TOCs I posted here and here to confirm that I wasn't kidding when I said that things would change. Things might change in the above TOC, too, but this one will be a lot more stable than the earlier versions.

The most recent Item I wrote was "Distinguish () and {} when creating objects." I blogged about one aspect of this topic here, and I thought you might be interested to see what I came up with. I've therefore made the current draft of this Item available, and I welcome your comments on it. Like almost all Items I've written, it's too long, so I'm especially interested in suggestions on how I can make it shorter, but I welcome all suggestions for improvement.

As things stand now, I'm hoping to have a full draft of the book by the end of April. We'll see how it goes.

Scott

Upcoming Public Presentations

2014 is shaping up to be a year with more public presentations than usual. As always, you can find links to all my scheduled talks at my Upcoming Talks page, but here are the ones scheduled so far:

On May 22, I'll giving a keynote address at the D Conference, The Last Thing D Needs. That'll take place in Meno Park, CA. I'm deliberately publishing no description of this talk, because I'd like to maintain the air of mystery engendered by the organizers' asking me to give a talk at conference on a programming language other than the one I'm normally associated with :-)

On June 4, I'll be giving four talks in Oslo at the Norwegian Developers Conference. They're introducing a C++ track this year, and two of my talks will be part of that: Effective Modern C++ and C++ Type Deduction and Why You Care. Of the other two, one will be on interface design (Better Software--No Matter What: The Most Important Design Guideline), and the other will be on performance (CPU Caches and Why You Care).

On September 17, I'll again present CPU Caches and Why You Care, this time in Bala Cynwyd, PA (near Philadelphia). That will be an evening presentation sponsored by Susquehanna International Group.

On October 7-8, I'll be doing a two-day seminar in London for Learning Connexions on material taken from the book I'm currently working on. The seminar is Effective C++11/14 Programming.


There are other presentations in the planning stages, so there will be more to come. To keep in the loop, stay tuned to this blog or do a periodic fly-by of my Upcoming Talks page.

I hope to see you at one or more of these presentations.

Scott

Thursday, March 13, 2014

A Concern about the Rule of Zero

The Rule of Zero, coined, as far as I know, by R. Martinho Fernandes in this blog post, is:
Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership. Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.
By "not have," Martinho really means "not declare", because all classes have destructors, one way or another, and the blog post makes clear that Martinho expects a non-resource-handling class to have the copying and moving behavior exhibited by its data members (and hence to have the corresponding functions, assuming such functionality is employed). If we revise the wording of the rule in accord with s/have/declare/g, we get:
Classes that declare custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership. Other classes should not declare custom destructors, copy/move constructors or copy/move assignment operators.
This advice makes me a bit uncomfortable.

In concept, it's great. Morally, I'm on board. I agree that classes that don't manage resources should be designed so that the compiler-generated functions for copying, moving, and destruction do the right things. I'm just not sure that taking advantage of that by not declaring any of these functions is a good idea.

Consider a class Widget that doesn't do resource management. Per the Rule of Zero, it declares none of the five special functions covered by the rule. Further assume that its data members are both copyable and movable. Widget objects are therefore copyable and movable, too.
class Widget {
public:
  ...                // no dtor or copy or move functions
};
Wonderful. Life is good.

Now assume something in the software doesn't work the way it should. It could be a behavioral problem (i.e., your run-of-the-mill bug) or it could be a performance problem. Either way, debugging ensues. Let's assume that during debugging, it becomes convenient to temporarily add a destructor to do something like produce a log message for tracing purposes:
class Widget {
public:
  ~Widget();         // temporary destructor
  ...                // no copy or move functions
};
The addition of the destructor has the side effect of disabling generation of the move functions, but because Widget is copyable, all the code that used to generate moves will now generate copies. In other words, adding a destructor to the class has caused presumably-efficient moves to be silently replaced with presumably-less-efficient copies. That strikes me as the kind of thing that (1) is likely to surprise people and (2) could really complicate debugging. Hence my discomfort.

I'm inclined to recommend that a better way to rely on the compiler-generated copy and move functions is to expressly say that they do the right thing--to define them via =default:
class Widget {
public:
  Widget(const Widget&) = default;
  Widget(Widget&&) = default;

  Widget& operator=(const Widget&) = default;
  Widget& operator=(Widget&&) = default;

  ...
};
With this approach, the spirit of the Rule of Zero remains: classes that don't manage resources should be designed so that the compiler-generated functions for copying, moving, and destruction do the right things. But instead of expressing this by not declaring those functions, it's expressed by declaring them explicitly and equally explicitly opting in to the compiler-generated implementations.

What do you think? Does this advice make sense? Should the Rule of Zero perhaps be the Rule of the Five defaults?

Thanks,

Scott

Saturday, March 8, 2014

If braced initializers have no type, why is the committee so insistent on deducing one for them?

I'm one Item away from having five full draft chapters for my book on effective use of C++11 and C++14. That's one Item away from being about 75% done. I'd really like to get that Item behind me.

The prospective Item title is "Distinguish () and {} when creating objects." I've put off writing it for months, because, frankly, I think the technical situation is a mess, and the advice I have isn't as helpful as I'd like. There are a number of aspects to the mess, but part of the problem is that a braced initializer such as "{ 10, 20, 30}" has no type, yet the C++11 and C++14 standards insist on deducing one for it. But only sometimes. Not always. Not for template arguments. Not for function return types. Not for auto parameters to lambda expressions. Only for auto variables. Which is why this is the case:

auto x = { 5 };    // "{ 5 }" has no type,
                   // but x's type is std::initializer_list

In this area, the standard makes no distinction between copy initialization (using "=", as in the example above) and direct initialization (without "=", as in the example on the next line), so:

auto y { 5 };      // y's type is also std::initializer_list

I've never understood why there's a special type deduction rule for braced initializers in the context of an auto variable, but, hey, I'm just a grunt on the ground. My job isn't to make the rules, it's to report them. 

There's an Item in my draft book devoted to the interaction of auto variables and braced initializers, because that interaction confuses everybody. Even so-called experts like me and Herb Sutter have known the thrill of publishing blog posts with incorrect code, because we forgot that 

int x1 = 5;
int x2(5);
int x3{ 5 };
int x4 = { 5 };

all do the same thing, but

auto x1 = 5;       // deduced type is int
auto x2(5);        // deduced type is int
auto x3{ 5 };      // deduced type is std::initializer_list
auto x4 = { 5 };   // deduced type is std::initializer_list

don't.

The committee is not unaware of this issue. The abstract for N3681 says, "This paper proposes to change a brace-initialized auto to not deduce to an initializer list..." That is, it proposes getting rid of type deduction for a construct that doesn't have a type and thereby eliminating a provision of C++ that confuses everybody. (As you can probably tell, I'm in favor of this proposal.)

The successors to N3681, N3912 and N3922, propose getting rid of this behavior only for direct initialization, thus introducing, in my view, even more confusion into an area that already has plenty. Under the proposed new rules, this would be the situation:

auto x1 = 5;      // deduced type is int
auto x2(5);       // deduced type is int
auto x3{ 5 };     // deduced type is int
auto x4 = { 5 };  // deduced type is std::initializer_list

Can somebody please explain to me why it' s so important to have a special rule for deducing a type for a braced initializer list for auto variables using copy initialization, but not important enough for auto variables using direct initialization, auto parameters to lambda expressions, or auto function return types?


Scott