Automatically created motion constructor with non-moving elements

I got into a situation that is quite interesting as the code that I work on compilers, although I am surprised that this is so, I would like to ask you about your reception.

The situation is as follows. I have a class with remote move and copy constructs that have custom 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 a single member. In this class, I defined the copy constructor, but I saved the default move constructor and defined the assignment operator by calling 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, given that it cannot just call the move or copy constructor A? I am using gcc 4.8.4.

+5
source share
1 answer

My initial answer was wrong, so I'm starting.


In [class.copy] we have:

By default, the copy / move for class X is defined as deleted (8.4.3) if X has:
- [...]
- a potentially constructed subobject type M (or its array) that cannot be copied / moved because (13.3), in relation to the corresponding constructor of Ms, leads to ambiguity or the function is deleted or inaccessible from the default constructor,
- [...]

This marker point applies to B(B&& other) = default; , therefore, the move constructor is defined as remote. This would seem to violate compilation using std::move() , but we also have (with permission defect 1402 ):

The default move constructor, which is defined as remote, is ignored by overload resolution (13.3, 13.4). [Note: The constructor with the remote move would otherwise interfere with the initialization from the r-value, which it could use instead, create a copy. -end note]

Ignoring is the key. Thus, when we do:

 B b1; B b2(std::move(b1)); 

Although the move constructor for B has been removed, this code is well-formed because the move constructor is simply not involved in overload resolution, and the copy constructor is called instead. So B is MoveConstructible - even if you can't build it through your move constructor.

+7
source

All Articles