Take for example this code:
#include <type_traits>
#include <iostream>
struct Foo
{
Foo() = default;
Foo(Foo&&) = delete;
Foo(const Foo&) noexcept
{
std::cout << "copy!" << std::endl;
};
};
struct Bar : Foo {};
static_assert(!std::is_move_constructible_v<Foo>, "Foo shouldn't be move constructible");
// This would error if uncommented
//static_assert(!std::is_move_constructible_v<Bar>, "Bar shouldn't be move constructible");
int main()
{
Bar bar {};
Bar barTwo { std::move(bar) };
// prints "copy!"
}
Because Bar is derived from Foo, it doesn't have a move constructor. It is still constructible by using the copy constructor. I learned why it chooses the copy constructor from another answer:
if
y
is of typeS
, thenstd::move(y)
, of typeS&&
, is reference compatible with typeS&
. ThusS x(std::move(y))
is perfectly valid and call the copy constructorS::S(const S&)
.
So I understand why a rvalue "downgrades" from moving to a lvalue copying, and thus why std::is_move_constructible
returns true. However, is there a way to detect if a type is truly move constructible excluding the copy constructor?