You can declare a type which has a defaulted copy constructor, but a deleted move constructor.
struct T
{
T() = default;
T(const T&) = default;
T(T&&) = delete;
};
This is perfectly legal, but it does not do what the user intended. This is because in C++, copying is considered a proper superset of moving. That is, any class which can be copied can be moved as well.
This comes about because if you delete a move constructor, an rvalue reference will bind to `const T&` instead of the deleted `T&&` constructor. Which means this is still legal:
T t{};
T t2{std::move(t));
The problem with this comes from type composition. Given `T` above, consider `U`:
struct U
{
T t_;
U() = default;
U(const U&) = default;
U(U&&) = default;
};
By the rules of implicit move constructor generation, U will be unable to generate a move constructor, since it would have to call the deleted move constructor of T. And therefore, U's move constructor will be deleted. This doesn't make sense to the user, because `is_move_constructible<T>` would be `true`.
However, at the very least, this U is functional. It is copyable and therefore moveable. Just not necessarily efficiently if it contains types that have specialized move constructors.
But what happens when we do this:
struct U
{
T t_;
unique_ptr<int> up_;
U() = default;
U(const U&) = default;
U(U&&) = default;
};
Well... now what? Since `T` has no move constructor, U's move constructor will be deleted. And since `unique_ptr` has no copy constructor, U's copy constructor will be deleted.
I would call this confusion a defect. It is clear from the standard that any type which is copyable is supposed to also be moveable. Yet a type which is moveable is unable to store move-only members and still be moveable.
Personally, I wish we could just forbid copy-but-no-move types altogether. Make them a compiler error. But that's probably bad for some reason.
The simplest way to resolve this is to make implicit move constructor generation call a subobject's copy constructor if that subobject's move constructor is deleted (obviously unless the copy constructor is also deleted).