Consider the following example:
#include <iostream>
#include <string>
#include <utility>
template <typename Base> struct Foo : public Base {
using Base::Base;
};
struct Bar {
Bar(const Bar&) { }
Bar(Bar&&) = delete;
};
int main() {
std::cout << std::is_move_constructible<Bar>::value << std::endl; // NO
std::cout << std::is_move_constructible<Foo<Bar>>::value << std::endl; // YES. Why?!
}
Why does the compiler generate a move constructor despite the base class being non-move-constructible?
Is that in the standard or is it a compiler bug? Is it possible to "perfectly propagate" move construction from base to derived class?
Because:
A defaulted move constructor that is defined as deleted is ignored by overload resolution.
([class.copy]/11)
Bar
's move constructor is explicitly deleted, so Bar
cannot be moved. But Foo<Bar>
's move constructor is implicitly deleted after being implicitly declared as defaulted, due to the fact that the Bar
member cannot be moved. Therefore Foo<Bar>
can be moved using its copy constructor.
Edit: Note that Foo<Bar>
inherits the set of constructors Bar::Bar
, including its copy and move constructors, Bar(const Bar&)
and Bar(Bar&&)
. But Foo<Bar>
also has its own, implicitly declared copy and move constructors, Foo<Bar>(const Foo<Bar>&)
and Foo<Bar>(Foo<Bar>&&)
, the latter being ignored by overload resolution for the reasons described above. When attempting to move-construct a Foo<Bar>
object, the implicitly declared copy constructor Foo<Bar>(const Foo<Bar>&)
is the best match. The inherited constructors are ranked lower by overload resolution.