Recently, Michal Mocny published an article applauding the C++ Committee’s recent decision to keep implicitly-generated move operations in the C++0x standard. I disagree, and I think it raises some really interesting questions about the standardization process and what’s next for C++, which I’ll discuss at the end.
For those of you who missed my previous article on the topic, the quick skinny is that Scott Meyers discovered that implicitly generating move-constructors and -assignment operators could break class invariants in correct code. The decision in Batavia was to tighten the conditions under which compilers could generate these operations, but not to remove the implicit move generation feature altogether.
So what’s wrong with that?
First, as was acknowledged by everybody in the room at the time of the vote,
Implicitly generated move operations will still break carefully-engineered, working C++03 code when recompiled under C++0x , no matter how we tighten the rules. The only way to avoid all such breakage is to tighten the rules “all the way,” i.e. drop the implicit move feature.
When I say “break”, I mean it in two ways:
- It’s going to cause actual crashes, asserts, and other visible misbehavior.
- It’s going to silently alter the invariants of some correct classes from what they have been designed and documented to be
I don’t know which one is worse, but I’m particularly worried about the effect of #2 on the ability of large software projects to transition to C++0x.
Cure: Worse Than Disease
Second, we’ve saved the patient’s life, but have left him in a condition so impaired that he really can’t be alone without supervision. By further limiting the cases in which we’ll implicitly generate move operations, the feature will only usefully “kick in” about as often as it will break code. As I said in the room on the day of the vote, it would have been much better to boldly break existing code and have implicit move generation that would actually be commonly useful, like implicit copy generation is now. Then everyone would know that it’s something they have to pay attention to, but it would be worth the trouble. The decision made in Batavia leaves us with an implicit move that’s hardly ever going to be a factor (and so will be easily overlooked), but when it is a factor, is just about as likely to hurt as it is to help. This is the kind of rare “gotcha” that keeps C++ an “experts-only language” in some peoples’ eyes.
How Did This Happen?
It’s very disappointing, but I see this a lot. While our emphasis on consensus is one of our great strengths, the C++ committee is extremely conflict-averse. The natural inclination of a group to choose what looks like a moderate compromise rather than anything that appears to be an extreme position can lead to bad decisions. When disagreements arise, there is a strong tendency to “cut a middle course,” even if it would be better to make a more definitive choice. In this case, the committee was offered a choice between
- leaving the implicit move feature alone
- limiting its effects, or
- removing it
and, naturally, we chose #2.
Another problem is that at the beginning of this 13-year design process, the committee did not reach a consensus about what kinds of code breakage would be acceptable. Some were absolutely convinced that we had to be able to maintain binary backward-compatibility, some thought source backward-compatibility was paramount, and some, like me, thought some code breakage should be on the table. However, no decision was reached at the time.
Since we’re apparently willing to break code, it seems clear now that the third group won the argument. Unfortunately, we missed the opportunity to decide what kinds of code we could break. For example, is it acceptable to break code if we aren’t sure we can generate compiler warnings to help people through the transition? Is it acceptable to break code that follows all the common coding guidelines, or do we have to be able to say with conviction about the cases that break, “of course, if you do that, you deserve what you get?” I’m not even sure these are all the right questions, but we haven’t really begun to ponder what the right questions are. So here we are near the end of this whole effort, having painted ourselves into a corner, with no agreed-upon rules on how to navigate our way out of it.
I hope, at least, that we figure it out before the next round of language changes.