1

我在 C++ 入门第 5 版的最后一个附录中。(解决方案部分):

这是那里的一个例子:

假设Y是一个定义了自己的复制构造函数但没有移动构造函数的类

 struct hasY{
     hasY() = default;
     hasY(hasY&&) = default;
     Y mem; // hasY will have a deleted move constructor
 };
 hasY hy, hy2 = std::move(hy); // error: move constructor is deleted

我添加了以下定义struct Y

struct Y{
    Y() = default;
    Y(Y const&){cout << "Y's cpy-ctor\n";}
};
  • 当我运行程序时,它工作得很好,并且不会抱怨hasY删除的移动构造函数。我得到输出:

     Y's cpy-ctor
    
  • 所以我认为这是可行的,因为类型Y的对象是通过复制构造函数移动的,而不是相反的。因此调用hasY'move-ctor 调用Y'move-ctor 被删除然后编译器使用Y'copy-ctor 移动该元素。我对吗?引导我。谢谢!

4

1 回答 1

3

我已经添加了struct Y

这是书中提到的,但不是那种会触发这种行为的类型。您Y没有删除的移动构造函数。修改后:

struct Y{
    Y() = default;
    Y(Y const&){cout << "Y's cpy-ctor\n";}
    Y(Y &&) = delete;
};

你会看到书中描述的行为。

删除移动构造函数和抑制移动构造函数之间存在很大差异。您创建的Y有一个用户定义的复制构造函数,因此移动操作被禁止。因此,它们在“移动”时不参与重载解决方案,并且选择复制 c'tor 作为后备。

已删除的移动构造函数(任何已删除的函数)参与重载决议。它可以被选为最佳候选者,一旦它被选中,我们就有一个程序引用了一个已删除的函数并且格式错误。

这是许多人偶然发现的一个好点。这里的经验法则是“回退”行为仅适用于不涉及移动操作的类型(递归应用于基数和成员)。一旦声明了移动操作(再次,甚至递归地),行为将相应地调整,因为我们现在选择了。

我建议阅读Howard Hinnant 的这个答案。他是直接负责将移动语义引入 C++11 的人员之一。你会注意到他建议永远不要删除移动操作。它可能导致程序表现出令人沮丧的行为。初级读物的作者可能预期这种行为是由于移动语义如何表现的预标准迭代。

于 2021-10-14T23:36:48.790 回答