为什么移动构造函数和移动标准库的赋值运算符会使对象从未指定的状态移开?

ant*_*_rh 7 c++ move c++-standard-library move-semantics

在C++标准库中有一个移动构造函数和移动赋值运算符的特殊描述,它表示数据移出的对象在调用后保持有效但未指定的状态.为什么?我坦白地说不明白.这是我直觉上没有想到的.真的,如果我在现实世界中将某个地方从一个地方移动到另一个地方,我搬离的地方就是空的(是的,有效的),直到我搬到那里一些新东西.为什么在C++世界中应该有所不同?

例如,根据实现,以下代码:

std::vector<int> a {1, 2, 3};
std::vector<int> b {4, 5, 6};
a = std::move(b);
Run Code Online (Sandbox Code Playgroud)

可能等同于下一个代码:

std::vector<int> a {1, 2, 3};
std::vector<int> b {4, 5, 6};
a.swap(b);
Run Code Online (Sandbox Code Playgroud)

这真的是我没想到的.如果我将数据从一个向量移动到另一个向量,我期望向量移动数据为空(零大小).

据我所知,标准C++库的GCC实现在移动后将向量保持为空状态.为什么不将此行为作为标准的一部分?

将对象保留为未指定状态的原因是什么.如果是为了优化,那也有点奇怪.我可以对未指定状态的对象做的唯一合理的事情是清除它(好吧,我可以得到向量的大小,我可以打印它的内容,但由于内容未指定,我不需要它).因此,对象将以任何方式由我手动或通过调用赋值运算符或析构函数清除.我更喜欢自己清除它,因为我希望它能被清除.但那是双重呼唤clear.优化在哪里?

Lig*_*ica 8

在C++标准库中有一个移动构造函数和移动赋值运算符的特殊描述,它表示数据移出的对象在调用后保持有效但未指定的状态.为什么?我坦白地说不明白.这是我直觉上没有想到的.真的,如果我在现实世界中将某个地方从一个地方移动到另一个地方,我搬离的地方就是空的(是的,有效的),直到我搬到那里一些新东西.为什么在C++世界中应该有所不同?

事实并非如此.

但是你没有考虑到"移动"并不总是一个举动.std::array例如,从a移动数据时会发生什么?不多.由于数组就地存储了数据,因此没有交换指针,移动就成了副本.因此,虽然图书馆可能会破坏原始数据,但这样做并没有任何意义,所以标准不会比说"我们不保证你得到的"更进一步.

一个真实的例子是std::string当前存储其内容不在动态分配的内存块中,而是存储在一个小的自动分配的内存块中(这通常称为小字符串优化).像数组一样,没有办法真正"移动"这些信息; 它必须被复制.该字符串可能为零出来之后,它可以其减少length计数器清零,但为什么强迫其用户是运行费用是多少?

因此,可以根据具体情况对移动后的容器的状态做出更有力的保证,但只能通过人为限制实施(并减少优化机会),坦率地说没有充分的理由.

真实世界的类比可以作为一个思想实验很有趣,但使用它们来实际理解编程语言的行为是愚蠢的.