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?
C++11, or rather n3485, [class.copy]/9:
and /11:
As
with_copy
is not trivially copyable,foo
will have no move-constructor (it would be defined as deleted, therefore it won't be implicitly declared).C++1y, or rather github repo commit e31867c0 from 2013-11-12; incorporating DR1402:
/9:
and /11:
Here,
foo
will have a move-constructor.I'm not quite sure what you tested but it
foo
is surely both move assignable and move constructible. Admittedly, this doesn't say anything about a move constructor or a move assignment being accessible, just that construction or assignment from an rvalue works. Both clang (clang version 3.5 (trunk 196718)) and gcc (gcc version 4.9.0 20131031 (experimental) (GCC)) agree with this assessment. This is the complete source I tried: