【问题标题】:std::move(std::string) not making passed argument to empty statestd::move(std::string) 没有将传递的参数设置为空状态
【发布时间】:2020-12-23 15:15:07
【问题描述】:

我有点困惑为什么std::move(std::string) 没有将传递的std::string 参数设置为空状态(我的意思是std::string 的大小为0,并且它的内部缓冲区在调用std::move(std::string) 后指向nullptr )。这是一个示例代码

#include <iostream>
#include <string>

void print(std::string& str) {
    std::cout << "lref\n";
    std::cout << str << "\n" << std::endl;
}

void print(const std::string& str) {
    std::cout << "const lref\n";
    std::cout << str << "\n" << std::endl;
}

void print(std::string&& str) {
    std::cout << "rref\n";
    std::cout << str << "\n" << std::endl;
}

int main() {
    std::string str_a = "Hello, ";
    std::string str_b = "world!";
    
    print(str_a);
    print(str_b);
    print(str_a + str_b);
    
    print(std::move(str_a));
    print(str_a); // was expecting str_a to be empty but still prints Hello, 
    
    return 0;
}

【问题讨论】:

  • 不应该那样做。被移动的对象应该处于有效但不确定的状态。
  • 你预期错了。 std::move 只创建一个右值引用,它不会“移动”任何东西——这必须通过移动构造函数或移动赋值来完成。
  • 这能回答你的问题吗? Why does moving std::optional not reset state
  • 例如,您的实现可能会进行短字符串优化 (SSO),因此甚至不指向缓冲区,只需在内部存储 chars。在这种情况下,没有什么可以释放,也没有双重释放相同(不存在的)缓冲区的危险,所以它不需要清除任何东西。但同样,在实践中它是否这样做并不重要,因为你不应该在移动后使用该对象,除非你首先将它重新分配/重置为已知状态,这样的移动操作不会'不给。
  • 即使字符串实际上被移出(因为它在此代码中不是),该字符串仍然是有效的,但未指定状态。我发现这通常是一个很好的期望:如果您 std::move 一个对象,则该对象适合重新分配或销毁,仅此而已......对某些类型的对象有一些额外的保证(例如, std::vector 和 std::unique_ptr 和 std::shared_ptr 有更强的保证)。 Howard Hinnant 不喜欢我的“放手”的看法,他会说(转述)从程序员保证的任何状态移动的对象。

标签: c++


【解决方案1】:

std::move 没有移动任何东西。它将其论点转换为rvalue-reference。要实际移动,您需要将str 作为rvalue-reference 传递以移动另一个字符串的构造函数(或移动赋值),如下所示。

std::string new_str(std::move(str));
print(str);

【讨论】:

  • 即使这样,类的移动构造函数或移动赋值运算符也可能选择不实际清除值,因为按照惯例移动不必这样做(但他们应该记录他们所做的事情无论哪种方式)
  • 他们真的应该记录下来吗?移动操作应该只将被移动的对象置于安全状态以被销毁。之后用户不应假设任何特定状态,而应停止使用已移动的对象。
  • @NicolasDusart 标准是否规定任何实现移动构造函数或移动赋值运算符的人都必须这样做?如果是这样,在哪里,它需要每个人还是只需要它自己的标准库?无论如何,即使标准确实对此设置了默认位置,我认为我可能会争辩说无论如何记录它是好的。可以简单地说“我按照 stdlib 约定所说的去做”或“我保证这些额外的东西”或“我做一些完全不同的事情”。有了这样好的文档,就不会出现许多这样的问题。
  • @underscore_d 如果他们想做一个最终处于确定状态的“移动”操作,最好通过一个特定的函数来提供它。在没有文档的情况下坚持对移动构造函数和赋值的平均 C++ 期望比具有非常好的文档的特定移动构造函数和赋值要好得多。如果人们看到正常的移动任务,他们不会倾向于阅读文档,它应该只是按照他们的期望去做。
  • @NicolasDusart 说的期望记录在哪里,这是我的观点 - 我找到了here - 我的另一点是,其他人记录他们自己的移动操作说'仍然不会受到伤害我们和 stdlib 在这里做的一样。我同意他们应该做同样的事情,但我不认为明确记录它会造成伤害。
猜你喜欢
  • 2016-09-10
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-21
  • 2021-11-07
  • 1970-01-01
相关资源
最近更新 更多