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?
There are claims that presence of move constructor can't be detected and on surface they seem to be correct -- the way
&&
binds toconst&
makes it impossible to tell which constructors are present in class' interface.Then it occurred to me -- move semantic in C++ isn't a separate semantic... It is an "alias" to a copy semantic, another "interface" that class implementer can "intercept" and provide alternative implementation. So the question "can we detect a presence of move ctor?" can be reformulated as "can we detect a presence of two copy interfaces?". Turns out we can achieve that by (ab)using overloading -- it fails to compile when there are two equally viable ways to construct an object and this fact can be detected with SFINAE.
30 lines of code are worth a thousand words:
Notes:
you probably should be able to confuse this logic with additional
const/volatile
overloads, so some additional work may be required heredoubt this magic works well with private/protected constructors -- another area to look at
doesn't seem to work on MSVC (as is tradition)
How to find out whether or not a type has a move constructor?
Assuming that the base class comes from the upstream, and the derived class is part of your application, there is no further decision you can make, once you decided to derive 'your' Bar from 'their' Foo.
It is the responsibility of the base class Foo to define its own constructors. That is an implementation detail of the base class. The same is true for the derived class. Constructors are not inherited. Trivially, both classes have full control over their own implementation.
So, if you want to have a move constructor in the derived class, just add one:
If you don't want any, delete it:
If you do the latter, you can also uncomment the second static_assert without getting an error.