std::move的未定义行为

Undefined behavior with std::move

本文关键字:未定义 move std      更新时间:2023-10-16

从cppreference的移动页面

除非另有说明,所有具有处于有效但未指定的状态。也就是说,只有没有先决条件的函数,比如赋值操作符,在对象从

移动后,可以安全地在对象上使用。

因此,从同一页面的示例中,下面的代码被认为是未定义行为

vector<string> v_string;
string example = "example";
v_string.push_back(move(example));
cout << example << endl;

MSVC将在控制台上不输出任何内容,但是如果我这样做

vector<int> v_int;
int number = 10;
v_int.push_back(move(number));
cout << number << endl;

将输出10。发生这种情况有什么原因吗?或者它总是未定义的行为?

未指定并不表示未定义。

根据c++ 11标准,17.3.26节:

有效但未指定状态一种未指定的对象状态,除非满足对象的不变量,并且对象上的操作按照其类型

指定的方式运行。

当对象处于有效状态时,您可以将其流式传输到输出,因为流式传输没有额外的先决条件。然而打印的内容是未指定的,所以它可能什么都不打印,或者打印您的父亲闻起来有接骨木浆果的味道。你不能安全地做的是使用带有附加前提条件的函数,比如back(),它额外要求字符串非空。有效的字符串可以为空。

对于未指定但有效的状态,包含旧值是完全可以接受的选项。对于基本类型,如int,简单的复制是执行移动的最有效方法。

还应该注意的是,int不是标准库对象,而是基本类型(如第3.9.1节所定义)。因此你方报价不适用

这是因为string可以通过窃取指向实际字符的指针来有效地从move d中获取,所以这就是编译器所做的,从string中留下move d"空"。int不能有效地move d,你只能复制一个。因此旧的int仍然存在。但这是不公开的。只是不要使用您的move从对象,不要依赖于未指定的行为。

未定义行为只是意味着

本国际标准对

行为没有要求。允许的未定义行为包括忽略情况完全无法预测的结果,在翻译过程中的行为或以文档化的方式执行程序环境(是否发出诊断消息),到终止翻译或执行(通过发出诊断消息)。许多错误的程序构造不会产生未定义的行为;

如果你感兴趣,可以看一看"关于未定义行为,每个C程序员都应该知道什么"。真是大开眼界。

在你的例子中,它不是未定义的行为,而是未指定的状态:在21.4.2.16中,c++标准定义了move构造函数的语义:

构造一个basic_string类的对象,如表69所示。存储分配器是由alloc构造的。在第二种形式中,str保持有效状态,值未指定。

"第二种形式"是move构造函数,因此字符串处于未指定状态。

这意味着对象必须处于满足不变量的状态,而其他任何状态都未指定。对于字符串,任何内容都可以