Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically generated move constructor with not movable members

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.

like image 435
Triskeldeian Avatar asked Aug 24 '15 15:08

Triskeldeian


People also ask

Are move constructors automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

Which constructor is automatically created by compiler?

In computer programming languages, the term default constructor can refer to a constructor that is automatically generated by the compiler in the absence of any programmer-defined constructors (e.g. in Java), and is usually a nullary constructor.

Does compiler provide default move constructor?

In C++, the compiler creates a default constructor if we don't define our own constructor. In C++, compiler created default constructor has an empty body, i.e., it doesn't assign default values to data members. However, in Java default constructors assign default values.

Is default constructor a Noexcept move?

Inheriting constructors and the implicitly-declared default constructors, copy constructors, move constructors, destructors, copy-assignment operators, move-assignment operators are all noexcept(true) by default, unless they are required to call a function that is noexcept(false) , in which case these functions are ...


1 Answers

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.

like image 64
Barry Avatar answered Sep 28 '22 08:09

Barry