Reputation: 628
I got in a situation which is quite interesting as the code I'm working on compiles even though I'm surprised it does so I would like to ask you for your take.
The situation is this. I have a class with deleted move and copy constructors, which has user-defined assignment operators:
struct A {
A() { }
A(const A&) = delete;
A(A&& ) = delete;
A& operator=(const A& ) { return *this; }
A& operator=(A&& ) { return *this; }
};
And I have another class with A
as the only member. In this class I defined the copy constructor but I kept the move constructor as default and defined the assignment operator through a call to the swap function:
class B{
public:
A a;
B()
: a{}
{ }
B(const B&)
: a{}
{ }
B(B&& other) = default;
};
int main() {
B b1;
B b2(std::move(b1)); // compiles??
}
Why does the default move constructor work, considering that it cannot simply call the move or copy constructor A? I am using gcc 4.8.4.
Upvotes: 6
Views: 539
Reputation: 303890
My original answer was wrong, so I'm starting over.
In [class.copy], we have:
A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:
— [...]
— a potentially constructed subobject type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— [...]
That bullet point applies to B(B&& other) = default;
, so that move constructor is defined as deleted. This would seem to break compilation with std::move()
, but we also have (via resolution of defect 1402):
A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4). [ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note ]
Ignoring is the key. Thus, when we do:
B b1;
B b2(std::move(b1));
Even though the move constructor for B
is deleted, this code is well-formed because the move constructor simply doesn't participate in overload resolution and the copy constructor is called instead. Thus, B
is MoveConstructible - even if you cannot construct it via its move constructor.
Upvotes: 7