理解std::array的移动语义

4

我正在尝试理解移动语义,因此我进行了以下测试:

#include <iostream>
#include <array>

using namespace std;

void tryToMove(array<double,3> && v) {
    array<double,3> v_ = std::move(v);
    std::cout << v_[0] << "  " << v_[1] << "  " << v_[2] <<'\n';
}

int main () {
    array<double,3> v{1,2,3};

    tryToMove(std::move(v));
    std::cout << v[0] << "  " << v[1] << "  " << v[2] <<'\n';
}

我原本期望在主函数的std::cout中出现段错误,因为v_应该已经在tryToMove中被移动了。但是,输出结果却是:

1 2 3
1 2 3

这里到底发生了什么?
谢谢!

2
移动语义与分段错误有什么关系?您是否预期未定义的行为?如果是,未定义的行为并不意味着代码“总会崩溃”。 - user3920237
2
当您在某个对象上调用std::move时,必须假定它已成功移动。标准规定,在移动后,对象处于有效但未指定的状态,因此数组中的值不是确定性的。虽然std::array的移动构造函数是隐式的,但它只会复制基础数组,而不会进行其他操作。 - David G
2个回答

16

我本以为会出现分段错误

首先,大多数导致分段错误的方法都是由于未定义的行为而产生的,在这种情况下任何事情都可能发生,因此您可能不应该期望任何特定的结果。

因为 v_ 应该被移动

std::array 包含其元素直接嵌入在其中(不像 std::vector 那样在堆上,由指针引用),因此您无法将其内容移动到任何地方。它们将始终在其中。

所以,您所做的只是从旧数组初始化一个新数组,您没有移动任何内容。新数组的元素将由 rvalue doubles 初始化,但这也不会移动任何东西,因为您无法“移动”double(它没有要传输的外部分配数据)。所以这只是每个元素的副本。


1
这些微妙的C++问题让我崩溃。谢谢你的回答! - Javi
3
如果你想学习,我不会将它视为微不足道的。 - user3920237
1
@JaviV,有些类对移动后的对象做出保证,但通常情况下,你不能对其进行任何操作,只能将其赋值或销毁。这是一个相当不错的经验法则。 - chris
2
@chris,我不同意那个经验法则,它比必要的保守得多。 - Jonathan Wakely
3
如果我有一个移动过的std::vector,没有任何阻止我调用clear()并重新使用它,例如。经验法则是“不要在没有检查先决条件的情况下执行具有先决条件的操作”。这比学习“除了赋值和销毁之外什么都不做”还要简单。 - Jonathan Wakely
显示剩余2条评论

1
问题在于你认为只要访问不属于你的内存,就会出现分段错误。
未定义行为意味着任何事情都可能发生。
在这种情况下,变量v的状态是未指定的,因此对其三个元素的访问可能有效,也可能无效。当它们有效时(由于基础数据很可能在这种情况下被复制),一切都很好;当它们无效时,你不能简单地假设结果将是分段错误。操作系统必须满足几个条件才能引发分段错误:这不是检测错误的魔法子弹。

1
但是内存是“你的”。 - juanchopanza
2
移动 int 的操作不是规范良好的吗?考虑到数组的移动构造函数的语义以及对于类型为 array<T, N>aa.size() == N 的不变量,我认为它似乎并不完全没有规定。 - juanchopanza
4
是的,"valid but unspecified state" 这个说法表示 "除非另有规定"(在整个标准中都是隐含的),而 std::array 的精确规定算作我的书中的"另有规定"。 - Jonathan Wakely
1
@JonathanWakely: 尽管如此,我更感兴趣的是帮助OP纠正关于UB的误解,而不是争论我们应该将实际推断视为标准命令的程度。 :) - Lightness Races in Orbit
1
对于那些说“读取项肯定没问题”的人,double的值在标准下可能包含陷阱表示,从它们中进行move操作可能会将double设置为所述陷阱表示(如果有的话,那么在调试中这样做将非常棒)。 进行读取可能会导致任何事情发生(例如seg fault或鼻部恶魔)。 写入仍然有效,arr.size()== N等。 - Yakk - Adam Nevraumont
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接