I'm familiar with the notion in c++ of the rule of 3, however since the release of C++11 I've seen some sources suggesting it should be extended to a "rule of 5", I.e. the move constructor and move assignment operator should also be implemented whenever the others are. What is the rationale behind such a rule? My understanding is that in most cases implementation of move semantics is only necessary as an optimization - am I wrong about this, or is the so-called rule of five about optimizing my code (and, therefore, substantially less important than the rule of 3, which is about avoiding pitfalls that can lead to unexpected behaviors)?
Move semantics were added to C++11 in such a way that the "rule of 3" is still valid and sufficient to avoid the pitfalls that it is meant to avoid (althogh there are better ways in most situations by applying the "rule of 0").
There are also no additional pitfalls added to the language that the "rule of 5" would help you avoid. In that sense, the rule of 5 is more about optimization than about avoiding pitfalls.
On the other hand, treating the "rule of 5" as an extension to the "rule of 3" does help you to remember that in C++11 there are two additional special member functions that you need to think about when your class does something special that needs a destructor or copy-constructor.
And although move semantics are an optimization, providing a move-constructor and/or move-assignment operator doesn't mean you are prematurely optimising, just like selecting the right algorithm isn't a premature optimization.
Adding support for move semantics to your class, when it makes sense, usually takes little effort and doesn't detract from the readability/maintainability of the code.
The Rule of Five is ex-idiomatic. It was only idiomatic for a very brief period before the Rule of Zero.
The principle of the Rule of 3 became obsolete when writing your own resource-handling classes became obsolete, which is when your compiler supports rvalue references. If you implement in terms of unique_ptr, which you can for virtually every resource because the deleter is so customizable, you only need a custom copy assignment and copy constructor- so two. If you need to support copying. There's no reason to implement your own destructor, move assignment operator, or move constructor in the vast majority of cases.
Rule of Three code isn't broken but it's much harder to maintain than Rule of Zero code and not as efficient either. Move operations offer correctness in many cases like
unique_ptr so you should strive to support them wherever possible.
It's copy operations that are now largely redundant in the face of moves, not the other way around.
In other words, unique_ptr is so flexible, and defaulted operations so useful, that there's hardly ever any reason to implement your own special members anymore. In fact, there's hardly any reason to implement your own resource handling classes. And since those classes virtually never exist, there's no reason to to have an idiom to construct them.