Why is the move constructor neither declared nor deleted with clang?

Viewed 1491

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 invoking unique_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?

2 Answers
Related