The queries "is_convertible_v move constructor deleted" and "c++ deleted move constructor makes class not convertible to itself", so I hope I'm not duplicating something thats already been answered... here goes...
I have the following class:
#include <type_traits>
struct OnlyCopyable {
OnlyCopyable() { }
~OnlyCopyable() { }
OnlyCopyable(const OnlyCopyable &src) { }
OnlyCopyable& operator=(const OnlyCopyable &other) { return *this; }
OnlyCopyable(OnlyCopyable &&other) = delete;
OnlyCopyable& operator=(OnlyCopyable &&other) = delete;
};
static_assert(std::is_convertible_v<OnlyCopyable, OnlyCopyable>);
It appears to not be convertible to itself. Why is this?
If I comment out the lines explicitly deleting the move operators it becomes convertible. Why is that?
CPPReference has this to say:
If the imaginary function definition
To test() { return std::declval<From>(); }
is well-formed, (that is, eitherstd::declval<From>()
can be converted toTo
using implicit conversions, or bothFrom
andTo
are possibly cv-qualifiedvoid
), provides the member constant value equal totrue
Which I read as meaning that a class with a copy constructor only should be convertible to itself... what did I miss? Thanks.
EDIT:
I investigated a bit further and defined, as per the above quote, the following:
OnlyCopyable test() { return std::declval<OnlyCopyable>(); }
And low and behold that doesn't work with error message error: use of deleted function 'OnlyCopyable::OnlyCopyable(OnlyCopyable&&)'
.
I can see why the above failed, because a deleted operator still takes part of overload resolution, and if chosen causes a fail.
This still puzzles me though... why is convertibility defined in this way? Surely the class is still convertible... just via copy and not move?
This still puzzles me though... why is convertibility defined in this way? Surely the class is still convertible... just via copy and not move?
I'd argue that it's nonsensical1 for a class to be copyable but not movable, so this isn't a case that the standard should care about. The test "To test() { return std::declval<From>(); }
is well formed" is simple, and that simplicity is valuable.
rvalues are more general than lvalues, because of lvalue-to-rvalue conversion.
If you don't define a move constructor, then overload resolution selects the copy constructor as the best viable function. Instead you have = delete
d it, which leaves it in overload resolution, and results in an error when it is selected as the best viable function.
struct ExplicitMove {
ExplicitMove(const ExplicitMove &src);
ExplicitMove& operator=(const ExplicitMove &other);
ExplicitMove(ExplicitMove &&other) : ExplicitMove(other) {}
ExplicitMove& operator=(ExplicitMove &&other) { return *this = other; }
// Other members...
};