c++ 移动具有不可移动构件的铅字

siotufzp  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(106)

在他的《C++ Move Semantics》一书中,Nicolai M. Josuttis指出,使用生成的移动构造函数移动包含可移动成员中的不可移动成员的对象,会移动除不可移动成员(被复制)之外的所有成员。下面是代码片段,它是书中示例的变体。

#include <iostream>

class Copyable {
    std::string name;
public:
    explicit Copyable(std::string name): name(std::move(name)) {}
    // Copying enabled
    Copyable(const Copyable& other): name(other.name) {
        std::cout << "Copyable copy ctor" << std::endl;
    }
    Copyable& operator=(const Copyable& other) {
        name=other.name;
        std::cout << "Copyable op=" << std::endl;
        return *this;
    }
    // Moving disabled with no copy fallback
    Copyable(Copyable&&) = delete;
    Copyable& operator=(Copyable&&) = delete;
};

class Movable {
    std::string name;
public:
    explicit Movable(std::string name): name(std::move(name)) {}
    // Copying enabled
    Movable(const Movable& other): name(other.name) {
        std::cout << "Movable copy ctor" << std::endl;
    }
    Movable& operator=(const Movable& other) {
        name=other.name;
        std::cout << "Movable op=" << std::endl;
        return *this;
    }
    // Moving enabled
    Movable(Movable&& other) noexcept: name(std::move(other.name)) {
        std::cout << "Movable move ctor" << std::endl;
    }
    Movable& operator=(Movable&& other) noexcept {
        name = std::move(other.name);
        std::cout << "Movable move op=" << std::endl;
        return *this;
    }
};

class Container {
    Copyable copyable;
    Movable movable;
public:
    Container(Copyable copyable, Movable movable): copyable(copyable), movable(std::move(movable)) {}
    // Both copying and moving enabled by default
};

int main() {
    Copyable c{"copyable"};
    Movable m{"movable"};
    Container container{c, m};
    Container container2{std::move(container)};
}

使用GCC在x86-64上编译,采用C++17标准,生成以下输出:
创建并初始化容器:

Copyable copy ctor
Movable copy ctor
Copyable copy ctor
Movable move ctor

移动容器:

Copyable copy ctor
Movable copy ctor

一旦容器移动,就不需要移动部件的移动执行器。根据书上的说法,movector应该被称为Movable member,对吗?

tct7dpnv

tct7dpnv1#

我不知道这本书的作者是什么意思,但这里引用了cppreference的一句话:
如果以下任一项为真,则类T的隐式声明或默认的移动构造函数被定义为已删除:

  • T具有不能移动的非静态数据成员(具有已删除、不可访问或不明确的移动构造函数);
  • [...]

因此Container的移动构造函数被隐式声明(也称为“生成”)为删除。你不能使用它。相反,std::move(container)绑定到复制构造函数的const引用,这就是所谓的。
请注意,如果您尝试像这样将移动构造函数声明为默认值,编译器应该会给予您一条错误消息:

Container(Container&& other) noexcept = default;

例如,GCC说:
错误:使用已删除的函数“Container:Container(Container&&)”
注意:“Container::Container(Container&&)”被隐式删除,因为默认定义将是格式不良的
你可以得到书中描述的行为,但你必须自己写。就像这样:

Container(Container&& other) :
    copyable(other.copyable),
    movable(std::move(other.movable)) {
}

……虽然我不知道你为什么要这么做。在一个具体的场景中,必须有一个很好的理由不能移动Copyable。公平点说a copyable-but-not-movable type does not seem to be very useful。尽管如此,我不会期望书中描述的行为是默认的(隐含的)行为。

相关问题