Consider the following classes.
struct with_copy {
with_copy() = default;
with_copy(with_copy const&) {}
with_copy& operator=(with_copy const&) { return *this; }
};
struct foo {
with_copy c;
std::unique_ptr<int> p;
};
- Does
with_copy
have a copy constructor? Yes. It was explicitly defined. - Does
with_copy
have a move constructor? No. The explicit copy constructor prevents it from being generated. - Does
with_copy
have a deleted move constructor? No. Not having a move constructor is not the same as having a deleted one. A deleted move constructor would make an attempt to move ill-formed instead of degenerating to a copy. - Is
with_copy
copyable? Yes. Its copy constructor is used for copies. - Is
with_copy
movable? Yes. Its copy constructor is used for moves.
... and now the tricky ones.
- Does
foo
have a copy constructor? Yes. It has a deleted one, as its defaulted definition would be ill-formed due to invokingunique_ptr
's deleted copy constructor. - Does
foo
have a move constructor? GCC says yes, clang says no. - Does
foo
have a deleted move constructor? Both GCC and clang say no. - Is
foo
copyable? No. Its copy constructor is deleted. - Is
foo
movable? GCC says yes, clang says no.
(The behaviour is similar when one considers assignment instead of construction.)
As far as I can see, GCC is correct. foo
should have a move constructor that performs a move on each member, which in with_copy
's case degenerates to a copy. Clang's behaviour seems quite ridiculous: I have an aggregate with two movable members, and yet my aggregate is an immovable brick.
Who's right?
See Question&Answers more detail:os