amc176
amc176

Reputation: 1544

Move constructor in terms of move assignment operator

In the codebase on our project, I found something like this:

struct MeshData {
    MeshData() {}
    MeshData(MeshData&& obj) { *this = std::move(obj); }
    MeshData& operator=(MeshData&& obj) {
        if (this != &obj) {
            indexes = std::move(obj.indexes);
        }
        return *this;
    }
    std::vector<int> indexes;
};

Implementing move construction in terms of the move assignment seems like a clever idea to me that reduces code duplication, but after looking for information I didn't find any especific advice regarding this.

My question is: Is this an antipattern or are there any situations where this shouldn't be done?

Upvotes: 3

Views: 1647

Answers (2)

user7860670
user7860670

Reputation: 37488

One of the pitfalls of this approach can manifest itself when move constructors and move operators of member variables may yield different results. For example this would happen if indexes was a vector using custom allocator with std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value evaluating to true. In this case dedicated move constructor would move both allocator and data without throwing exceptions while calling move assignment would result in new storage being allocated and all the items move constructed leading to considerable overhead and preventing move constructor from being no throwing.

Upvotes: 3

TartanLlama
TartanLlama

Reputation: 65600

This cannot be done if your type has data members with no default constructors, and should not be done if they have default constructors which are expensive:

struct NoDefaultCtor {
    NoDefaultCtor() = delete;
    NoDefaultCtor(int){}
};

struct Foo {
    Foo(Foo&& obj) { *this = std::move(obj); }
    Foo& operator=(Foo&& obj) {
        if (this != &obj) {
            thing = std::move(obj.thing);
        }
        return *this;
    }
    NoDefaultCtor thing;
};

gives this error:

<source>: In constructor 'Foo::Foo(Foo&&)':
<source>:10:20: error: use of deleted function 'NoDefaultCtor::NoDefaultCtor()'
     Foo(Foo&& obj) { *this = std::move(obj); }
                    ^
<source>:5:5: note: declared here
     NoDefaultCtor() = delete;

This is because all data members must be constructed before the body of the constructor is entered.

However, the best advice is to follow the Rule of Zero and avoid writing those special members at all.

Upvotes: 8

Related Questions