Wednesday, June 25, 2014

Another New Video

When I recently posted about five new videos, I didn't realize that Yandex had published my 11 June presentation at the Moscow C++ Party. There I gave a talk, "Why C++ Sails When the Vasa Sank," which is almost a bookend for the talk I gave at the D Conference last month.
"Why C++ Sails..." is my attempt to explain why C++, despite its size and complexity, continues to be a successful and important programming language.

My comments at the beginning of the video probably make more sense if you know that I had just been introduced in Russian, which is a language I don't know.

I think the double-wide format Yandex published that shows the speaker on one side of the video and the slides on the other works really well. That's overlooking, of course, that the speaker in this case is me. Yeesh. Talk about a need for a makeover...

I realize that other outlets (e.g., the isocpp.org blog) have already reported the existence of this video, and it's got at least two independent threads at reddit (here and here), but I wanted to hold off announcing it until it had been added to my web site's Online Videos and Past Talks pages, including links to downloadable video and presentation materials. Things have been a bit backed up here, hence the delay.

Scott

Tuesday, June 24, 2014

The Drawbacks of Implementing Move Assignment in Terms of Swap

More and more, I bump into people who, by default, want to implement move assignment in terms of swap. This disturbs me, because (1) it's often a pessimization in a context where optimization is important, and (2) it has some unpleasant behavioral implications as regards resource management.

Let's consider a simplified case of a container that contains a pointer to its contents, which are stored on the heap. I'm using a raw pointer, because I don't want to abstract anything away through the use of smart pointers.
class Container {     
public:
  Container& operator=(Container&& rhs);

private:
  int *pData;           // assume points to an array
};
Implementing the move assignment operator using std::swap, the code looks like this:
Container& Container::operator=(Container&& rhs)
{
  std::swap(pData, rhs.pData);
  return *this;
}
Swapping two pointers calls for three pointer assignments,
template<typename T>
void swap(T& lhs, T& rhs)
{
  auto temp(lhs);
  lhs = std::move(rhs);
  rhs = std::move(temp);
}
so the cost of implementing move assignment using swap is three pointer assignments.

However, if we assume that an empty container has a null pData pointer, move assignment can be implemented using only 2 pointer assignments:
Container& Container::operator=(Container&& rhs)
{
  delete [] pData;
  
  pData = rhs.pData;
  rhs.pData = nullptr;

  return *this;
}
I'm ignoring the delete for now, but we'll get back to it later. At this point, I want to observe that non-swap move assignment performs only 2/3rds the assignments of its swap-based cousin. That's important, because move operations should typically be as efficient as possible. Remember that they're optimizations of copy operations, and if you're not concerned about their efficiency, why not just omit them and let rvalues be copied? My feeling is that the fact that a class author went to the trouble of adding support for move operations is a sign that the author perceives the class to be one where speed is important. If that's the case, it seems unreasonable to pay a premium to put the object being moved from into the state of the target of the assignment when it's cheaper to put the object into a different, but equally valid (typically default-constructed), state. After all, the semantics of move assignment typically don't specify the state of the source object of the move after the assignment has been performed, so if callers can't rely on it having the state of the target object before the assignment, why pay for it?

But back to the delete. Regardless of which move assignment implementation is used, the delete will eventually be performed. If it doesn't take place in the move assignment operator for the object that's the target of the move assignment, it'll probably occur in the destructor of the object that's the source of the move assignment. The cost therefore doesn't vary between the implementations, but when you incur that cost does, and that can be important.

Suppose that Container objects typically use a lot of memory--enough that you have to worry about it. Now consider the following scenario:
{
  Container c1, c2;
  ...
  c1 = std::move(c2);
  ...
}
In the non-swap implementation of move assignment, c1's memory is released at the point of the assignment, but in the swap version, that memory becomes associated with c2, and the memory may not be released until the end of the scope. That might well surprise the caller, who could hardly be blamed for thinking that when an assignment was made to c1, c1's old resources would be released. And it could certainly increase the maximum amount of memory used by the application at any given time.

In my experience, the impact of move-assignment-by-swap on the timing of resource release isn't as well known as it should be, even though the issue was well described many years ago (e.g., Thomas Becker  here and David Abrahams here).

Now, bear in mind that I said at the outset that I was disturbed by people who, by default, want to implement move assignment in terms of swap. I have no issue with developers who, consciously aware of the performance and behavioral implications of move-assignment-via-swap, choose to use it anyway. For some types, it may be a perfectly valid implementation choice. My concern is that it's gaining a reputation as the way to implement move assignment, and I don't think that's good for C++.

Am I mis-analyzing the situation?

Scott

Effective Modern C++ Status Report

In recent days, two major milestones for Effective Modern C++ have been achieved.  First, I sent a draft of the book's Introduction out for technical review. That was the last part of the book to be written, so I finally have a full draft manuscript. Second, I received an image of the book cover from my publisher, so I now know what the book will look like. My editor informs me that I'm the common crane, which elicited the same response from me and my wife: "common?!"

Working with O'Reilly is new for me. My past C++ books were published by Addison-Wesley, and my relationship with AW continues (both for past books as well as for books in my Effective Software Development Series). For Effective Modern C++, however, I concluded that O'Reilly was a better fit for me, and I'm quite pleased to be working with them.

A complete draft manuscript and a front cover design do not a book make. There's still a lot of work to be done, and I thought you might be interested in the schedule for it:
  • Now until July 31: I revise the manuscript to take comments from my technical reviewers into account. I currently have over 70 reviewer email messages (some extremely long and detailed) waiting to be processed, and I expect that number to increase significantly by the end of this month, which is when I've requested that reviewers get me their remarks.
  • August 1-15: I produce the book's index. This is my least favorite part of book creation, and I warn my wife well in advance that I'll be unusually irritating to be around during this time. Coming up with terms to index, including text rotations (e.g., "universal references, versus rvalue references," "rvalue references, versus universal references," "references, rvalue vs. universal") and page ranges is tedious, at best, and the tools available to get this information into a manuscript (e.g., into Microsoft Word, which is what I'm using) are primitive to the point of sadistic. There are professional indexers, but my experience (as well as that of other authors I know) has been that if you want a useful index for a book on C++, the content has to be created by the author. How practical it would be to have me identify the content and somebody else do the entry into the manuscript, I don't know. Index preparation is highly iterative, so the process would be more collaboration than hand-off, and my one prior experience with index collaboration did not go well.
  • August 16-30: A copyeditor goes over the entire book, looking for errors and inconsistencies in grammar, spelling, style, labeling, and more. No matter how clean you think your manuscript is and no matter how many people you've had review it, getting it back from a copyeditor is always a humbling experience.
  • September 1-5: I edit the manuscript one last time, taking the copyeditor's comments into account.
  • September 5: I deliver a final manuscript to O'Reilly. 
  • Early October: Digital versions of the book become available.
  • Late October: Print versions of the book become available.
If you're dying to see what's in the book, and you don't mind dealing with a manuscript that's in draft form (and hence contains technical errors, awkward prose, Item titles in need of revision, primitive diagrams, confusing explanations, and, I hope, some stuff in decent shape), Effective Modern C++ will be part of O'Reilly's Early Release Program, meaning you'll have a chance to see the book in the same form as my technical reviewers. You'll also be able to offer comments on it. As things stand now, the book is slated to be available in Early Release form the week of July 7th.


And now, if you'll excuse me, I have hundreds of reviewer comments to process...

Scott

Saturday, June 21, 2014

Five New Videos

It seems that every place I go these days, people are pointing a camera at me and pressing Record. As a result, there are five new videos available at my Online Videos page:
Happy video viewing!

Scott