Why does C++ compiler have more restriction on automatically generated move constructors than on automatically generated copy constructor or assignment operator ?
Automa
As far as I know, this is because of downward compatibility. Consider classes written in C++ (before C++11) and what would happen if C++11 would start to automatically generate move-ctors in parallel to existing copy-ctors or generally any other ctor. It would easily break existing code, by-passing the copy-ctor the author of that class wrote. Hence, the rules for generating a move-ctor where crafted to only apply to "safe" cases.
Here's the article from Dave Abrahams about why implicit move must go, which eventually led to the current rules of C++11.
And this is an example how it would fail:
// NOTE: This example assumes an implicitly generated move-ctor
class X
{
private:
std::vector<int> v;
public:
// invariant: v.size() == 5
X() : v(5) {}
~X()
{
std::cout << v[0] << std::endl;
}
};
int main()
{
std::vector<X> y;
// and here is where it would fail:
// X() is an rvalue: copied in C++03, moved in C++0x
// the classes' invariant breaks and the dtor will illegally access v[0].
y.push_back(X());
}
I believe backwards compatibility plays a big part here. If the user defines any of the "Rule of three" functions (copy ctor, copy assignment op, dtor), it can be assumed the class does some internal resource management. Implicitly defining a move constructor could suddenly make the class invalid when compiled under C++11.
Consider this example:
class Res
{
int *data;
public:
Res() : data(new int) {}
Res(const Res &arg) : data(new int(*arg.data)) {}
~Res() { delete data; }
};
Now if a default move constructor was generated for this class, its invocation would lead to a double deletion of data
.
As for the move assignment operator preventing default move constructor definitions: if the move assignment operator does something other than default one, it would most likely be wrong to use the default move constructor. That's just the "Rule of three"/"Rule of five" in effect.
When C++ was created, it was decided that default constructor, copy-constructor, assignment-operator and destructor would be generated automatically (unless provided). Why ? Because C++ compilers should be able to compile (most) C code with identical semantics, and that's how struct
work in C.
However, it was later noticed that whenever a user writes a custom destructor, she probably needs to write a custom copy-constructor/assignment-operator too; this is known as the Rule of Big Three. With hindsight, we can see that it could have been specified that the generated copy-constructor/assignment-operator/destructor would have only been generated if none of the 3 were user-provided, and it would have helped catch a lot of bugs... and still retain backward compatibility with C.
Therefore, as C++11 came around, it was decided that this time things would be done right: the new move-constructor and move-assignment-operator would only be generated automatically if it was clear that the user was not doing anything "special" with the class. Anything "special" being defined as redefining move/copy/destruction behavior.
To help with the case were people would be doing something special but still wanted "automatically generated" special methods, the = default
sugar-coating was added as well.
Unfortunately, for backward compatibility reasons, the C++ committee could not go back in time and change the rules of automatic generation for copy; I wish they had deprecated it to pave the way for the next version of the Standard, but I doubt they will. it is however deprecated (see §12.8/7 for the copy constructor for example, courtesy of @Nevin).