First I would say Mats Petersson's answer is better than the accepted one as it mentions the rationale.
Second, as a supplement, I want to elaborate a bit more.
The behavior of implicitly-declared (or defaulted) move ctor
From c++draft:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.
The condition when compiler implicitly declares a move ctor
From cppreference:
If no user-defined move constructors are provided for a class all of
the following is true:
- there are no user-declared copy constructors
- there are no user-declared copy assignment operators
- there are no user-declared move assignment operators
- there are no user-declared destructors
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&)
.
Why dtor(and many others) prevents implicitly-declared move ctor?
If we look at the above conditions, not only user-declared destructor prevents implicitly-declared move ctor, user-declared copy constructor, user-declared copy assignment operator and user-declared move assignment operator all has the same prevention effect.
The rationale, as Mats Petersson has pointed out, is that:
If the compiler thinks you might need to do something other than memberwise move in move operation, then it is not safe to assume you don't need it.
When there are user-declared destructors, which means there's some clean up work to do, then you probably want to do it with the moved-from object.
When there are user-declared move assignment operators, since it's also "moving" resources, you probably want to do the same in move ctor.
When there are user-declared copy constructors or copy assignment operators, this is the most interesting case. We know that move semantics allows us to keep value semantics while gaining performance optimization, and that move will "fall back" to copy when move ctor is not provided. In some way, move can been seen as the "optimized copy". Therefore, if the copy operation requires us to do something, it is likely that similar work is also needed in move operation.
Since in the above conditions, something other than memberwise move might needs to be done, the compiler will not assume you don't need it, and thus won't implicitly declare a move ctor.