- It may be valid(1). To address your specific issue about dtor/ctor lifetimes, yes that is valid(2). That is how the original implementation for vector worked.
- It may be a good idea (it probably isn't), but you may not want a canonical way.(3)
(1) There is controversy about whether or moves need to be valid in the self-move case.
Arguing for self-move safety is the position that code should be safe (duh), we certainly expect self assignment to be safe. Also some user experience reports that for many algorithms which use move, self-move is possible and tedious to check for.
Arguing against self-move safety is the position that the whole point of move semantics is time savings and for that reason moves should be as fast as possible. A self-move check can be expensive relative to the cost of a move. Note that the compiler will never generate self-move code, because natural (not casted) rvalues can't be self-moved. The only way to have a self-move is if a "std::move()" cast is explicitly invoked. This puts the burden on the caller of std::move() to either verify that self-move isn't involved or convince themselves that it isn't. Note also that it would be trivial to create a user defined equivalent of "std::move" that checked for self-move and then did nothing. If you don't support self-move, you might want to document that.
(2) This is not a template so you can know if the expression "new (this) Class(std::move(rhs));" can throw. If it can, then no, this isn't valid.
(3) This code may be a puzzle to maintainers, who might expect a more traditional swap approach, but there is a potential drawbacks to the swap approach. If the resources being released by the target need to be released as soon as possible (such as a mutex), then swap has the drawback that the resources are swapped into the move source object. If the move is the result of a call to "std::move()" the move source object may not be immediately disposed of. (Since this isn't a template you can know what resources are being freed. If memory is the only resource being freed, then this isn't an issue.)
A better approach might be to factor out the resource freeing code from the destructor and the resource moving code from the move constructor and then just call those (inlined) routines in this move assignment operator.
~Classornew (this) Class(std::move(rhs))throws?