Wednesday, July 24, 2013

Video for "The Universal Reference/Overloading Collision Conundrum"

Last Wednesday evening, I gave a talk at the Northwest C++ Users' Group entitled "The Universal Reference/Overloading Collision Conundrum." The purpose of the talk was to try out the information behind a guideline from Effective C++11/14 (the book I'm currently working on).  That guideline is "Avoid overloading on universal references." The video for that talk is now available.

From my perspective, the talk was a success, but that may not be apparent from the video. Things went fine for the first 12 minutes, and then...things went less fine. Bugs in the slides. Questions I didn't answer. Material I didn't have time to cover. All of which may--should--make you wonder how I define "success."

As a general rule, I like to test material in front of live audiences before I put it in my books. Presenting technical material live is perhaps the best way to get feedback on it. Not only does it offer attendees an opportunity to ask questions and make comments (direct feedback), it gives me a chance to see people's reactions (indirect feedback). Even if an audience asks no questions and makes no comments, looking into their faces tells me if they're engaged or bored and if they're following what I'm saying or are confused. Plus, the simple act of explaining something gives me a chance to see how well it flows in practice. It's quite common for me to think to myself "this just isn't working the way I'd hoped..." while I'm speaking, and places where I think that identify parts of the presentation where I need to go back and make revisions.

From the presentation at the NWC++UG (including some conversations I had with attendees afterwards), I took away two primary lessons. First, the guideline I was presenting ("Avoid overloading on universal references")  is both valid and useful. That was reassuring. Second, the technical justification I give for this guideline needs a fair amount of work. In particular, I need to avoid getting side-tracked too much by the issues surrounding overloading on universal references and its interaction with compiler-generated special functions. Both lesson will help me produce a better book, and that's why I consider the talk a success.

At the same time, I was disappointed that there were bugs in my slides. I have pretty much a zero-tolerance mindset for errors in presentation materials (as well as books and articles and other forms of publication), because authors (including me) have essentially unlimited amounts of time to prepare the materials prior to making them public. (If there's insufficient time to prepare the materials properly, my feeling is that you shouldn't agree to present or publish them.) To be honest, I was also surprised that my materials had the errors that they did, because I hadn't skimped on prep time or QA work. I really thought they were ready to go. I was mistaken. In the future, I'll clearly have to find ways to do a better job.

Since giving the talk, I've corrected and revised the materials, and the corrected slide set is available here.

I hope you enjoy the talk, rocky parts notwithstanding.

Scott



Wednesday, July 10, 2013

C++11 Training Materials Updated--Now With C++14 Info!

For the seventh time since originally releasing them over three years ago, I've updated my annotated training materials for "The New C++". Until this update, "the new C++" referred to C++11, but with this revision, I'm including treatment of several features from draft C++14 that I believe will make it into the new new standard. As far as I know, this makes my training materials the first "book-like" publication that covers features in C++14.

In accord with my "free updates for life" policy, people who've purchased these materials are entitled to (and should recently have received notification about) the updated version.

The latest revision of the materials contains the usual mish-mash of bug-fixes and presentation improvements (the changelog, which is delivered along with the latest matereials, has details), and those alone had me planning a release this summer. But when the C++14 CD was adopted in April, I knew I had to find a way to shoehorn some C++14 material into the course. The result is 20 new pages of information, including overviews of the following C++14 features:
  • Polymorphic lambdas (i.e., auto parameters).
  • Generalized lambda captures (makes "move capture" possible).
  • Variadic and perfect-forwarding lambdas.
  • Generalized function return type deduction (i.e., auto return types).
  • Reader/writer locks (i.e., std::shared_mutex and std::shared_lock).
There's also a page summarizing other C++14 features, along with a slew of references for people who want to read more about the new goodies in C++14.

If you haven't done so already, I hope you'll consider purchasing a copy of these materials. As always, a free sample PDF of the first ~40 pages is available here. Don't expect too much C++14 information in that sample, because the first serious treatment of C++14 features begins on slide 90. That's not me being coy. It's just how things worked out, given the flow of topics in the course.

Scott

Sunday, July 7, 2013

When decltype meets auto

C++11 has three sets of type deduction rules:
  • Those used in template type deduction.
  • Those used in auto type deduction.
  • Those used by decltype.
The rules for auto type deduction are the same as the rules for template type deduction, except that given a braced initializer such as { 1, 2, 3, 4 }, auto will deduce a std::initializer list type (in the case of { 1, 2, 3, 4 }, it will be std::initializer_list<int>), while template type deduction will fail. (I have no idea why type deduction for auto and for templates is not identical. If you know, please tell me!) The rules for decltype are more complicated, because they don't just distinguish between lvalues and rvalues, they also distinguish between id-expressions (i.e., expressions consisting only of identifiers, e.g., variable or parameter names) and non-id-expressions. For details on all these rules, consult this article by Thomas Becker, this article by me, or this article by Herb Sutter (for auto) and this one by Andrew Koenig (for decltype).

But that's for C++11, which, among the C++-obsessed, is rapidly approaching yawnworthiness. Fortunately, C++14 is on the horizon, and one of the new features sure to stifle even the strongest of  yawns is the ability to declare types using decltype(auto). This feature leads to two questions, only the first of which is rhetorical:
  1. You can declare what?
  2. During type deduction for decltype(auto), which type deduction rules are to be followed: those for auto or those for decltype?  Or does decltype(auto) have its own set of type deduction rules?
The answer is that decltype(auto)uses the decltype type deduction rules. The reason is that the type deduced by auto for an initializing expression strips the ref-qualifiers (i.e., lvalue references and rvalue references) and top-level cv-qualifiers (i.e., consts and volatiles) from the expression, but decltype does not. As a result, if you want the ref- and cv-qualifier stripping behavior, you can just write auto. If you don't, C++14 gives you the option of writing decltype(auto).

For variable declarations, this saves you the trouble of typing the initializing expression twice,
decltype(longAndComplexInitializingExpression) var =
  longAndComplexInitializingExpression;                     // C++11

decltype(auto) var = longAndComplexInitializingExpression;  // C++14
For auto function return types (another new C++14 feature), it's even more convenient. Consider a function template, grab, that authenticates a user and, assuming authentication doesn't throw, returns the result of indexing into some container-like object. Bearing in mind that some standard containers return lvalue references from their operator[] operations (e.g., std::vector, std::deque), while others return proxy objects (e.g., std::vector<bool>), and I believe it would be valid to define a container such that invoking operator[] on an rvalue would yield an rvalue reference, the proper "generic" way to declare this function in C++11 would be (I think):
template<typename ContainerType, typename IndexType>                //C++11
auto grab(ContainerType&& container, IndexType&& index) -> 
  decltype(std::forward<ContainerType>(container)[std::forward<IndexType>(index)]);
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

In C++14, I believe this can be simplified to the following, thanks to function return type deduction and decltype(auto):
template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}

Scott