在移动分配运算符方面移动构造函数

Move constructor in terms of move assignment operator

本文关键字:移动 构造函数 方面 分配 运算符      更新时间:2023-10-16

在我们项目的代码库中,我找到了类似的东西:

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;
};

从移动分配方面实施移动构造对我来说似乎是一个聪明的想法,可以减少代码重复,但是在寻找信息后,我没有找到任何特别的建议。

我的问题是:这是 antpattern 还是在任何情况下不应该这样做?

如果您的类型具有没有默认构造函数的数据成员,则不能执行此操作,如果它们具有昂贵的默认构造函数,则不应执行此操作:

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;
};

给出此错误:

<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;

这是因为必须在输入构造函数之前构建所有数据成员。

但是,最好的建议是遵循零的规则,避免写那些特殊成员。

当移动构造函数和成员变量的运算符可能产生不同的结果时,这种方法的陷阱之一就可以表现出来。例如,如果indexes是使用std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value评估true的自定义分配器的CC_1,则会发生这种情况。在这种情况下,专用移动构造函数将同时移动分配器和数据而无需抛出异常时,在调用移动分配的同时将导致新的存储分配,并且所有项目移动构造,导致大量开销,并防止移动构造函数没有投掷。

<</p> <</p> <</p>