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?
static_assert
that it's not copy-constructible. What's the use case for the code shown in the question as it currently reads? – Fustigatestruct baz: foo { baz(baz const&) = default; baz(&& other): baz(other) {} };
Incidently, this behavior is identical to yourbar
but it seems you expect somehow a different answer to the question. – Herculie