为什么可以从const std::optional中使用std::move移动值?

13
为什么下面的代码片段是合法的C++代码?这是一个纯理论问题 - 我没有任何实际用例。
#include <optional>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v{1, 2, 3};
    const std::optional<std::vector<int>> ov = v;
    const auto nv = std::move(ov.value());

    for (const auto& x : *ov) { std::cout << x; }
    for (const auto& x : nv) { std::cout << x; }
}

这会产生 123123,但我不明白原因。

  1. 为什么对 const optional 的值应用 std::move 是合法的?
  2. 为什么 optional ov 仍然持有 vector

ov.value() 是否在内部创建一个临时副本,然后再移动该副本?


10
把std::move()看作是对常量对象不做任何移动的转换。这里没有进行任何移动操作。 - Aedoro
2
请注意,由于向量的移动构造函数接受非常量右值引用,因此 nv 的构造使用复制构造函数完成。 - cptFracassa
使用std::move时,我喜欢跟随一种策略,即被移动的对象处于有效但未指定状态(适合重新赋值或销毁)。总体而言,在OP代码中,由于“ov”是“const”,因此“std::move”将不会导致对象被清空并留下一个空的外壳。尽管如此,我发现遵循这个惯用语,即(可能)被移动的对象就像是一只前鹦鹉工作得很好。总体而言。(当然也有例外,例如从std::unique_ptr中移动确实会使它保持在良好指定的状态。) - Eljay
std::vector<T> 也是一个例外 - 移动后的向量保证为空。 - Evg
对于 std::vector<T>,它始终处于空状态。 - Evg
显示剩余3条评论
1个回答

18
为什么对于const optional的值应用std::move是合法的?
当然可以,std::move只是将其转换为右值引用。你最终会得到一个指向const对象的右值引用,这并不奇怪。
optional ov为什么仍然持有vector?
因为它是const,不能被更改。
要移动一个对象,必须满足几个条件。std::move处理了最重要的一个条件,但其他所有条件也必须满足。
人们普遍认为在任何地方都使用std::move可以通过魔法保证对象从一个地方到另一个地方的超级高效移动。但这不是真的,这不是std::move的作用,它只是一个有时需要的转换。但这不是全部,只是最重要的部分。

8
这个回答是正确的,但似乎很努力地避免说出显而易见的事情:move 是一个误用的名称,因此用户在试图记住该函数实际执行的操作时应忽略该名称。 - Ruslan
1
我认为,“对常量对象的右值引用”是“不寻常的东西”,右值引用通常表示可以被掠夺的对象,但常量对象不能被掠夺。 - plugwash
4
你强调(通过重复两次)std::move不是移动所需的全部,但是您能否说出其他需要的东西?提供一个参考链接即可。 - Bergi

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