【问题标题】:Move a std::string to function param将 std::string 移动到函数参数
【发布时间】:2019-03-21 12:54:22
【问题描述】:

我想将一个大的 std::string 移动到类的成员中。

这是我的代码:

#include <cassert>
#include <string>
#include <iostream>
using namespace std;

class SomeClass {
public:
    void some_method(std::string&& str) {
        my_str = str;
    }

    std::string my_str;
};

int main() {
    SomeClass some_class;

    std::string large_str(100000, 'a');

    some_class.some_method(std::move(large_str));

    assert(large_str.empty());
    assert(!some_class.my_str.empty());
    return 0;
}

当移动之后,我预计large_str 是空的,但是这个代码断言在assert(large_str.empty()) 行失败。

我是不是误解了std::move的语义?

【问题讨论】:

    标签: c++ rvalue-reference


    【解决方案1】:

    我是不是误解了std::move的语义?

    部分,是的。您忘记将函数参数 str 转换为右值。在some_method 内,它又变成了一个左值。要解决这个问题:

    void some_method(std::string&& str) {
        my_str = std::move(str);
        //       ^^^^^^^^^ Necessary: invoke std::move a 2nd time
    }
    

    但请注意以下几点。 “移出”std::string 对象不是您应该在assert 上使用的。来自here,重载#8(强调我的):

    移动构造函数。使用移动语义构造带有 other 内容的字符串。 other 保持有效,但未指定状态

    您不希望您的程序依赖于未指定的状态(即使发生特定实现使std::string::size 在消耗其资源后返回 0)。

    【讨论】:

    • 在实践中,所有实现都会像这样移动大字符串的内容。因此,对于检查移动是否发生的测试代码来说,这是一个很好的机制。 (“未指定状态”是因为许多具有短字符串优化的实现将复制字符串,如果它保存在本地。)
    • @MartinBonner 好点。但这是特定于std::string,或者可能是标准容器。如果您经常看到关于“已迁移”对象的断言足以将其作为您不完全了解的某些代码库的习惯,该怎么办?下一步是不要assert 某些属性,而是将其也用于发布构建...有人可能会争辩说,出于一致性问题,不应该永远触摸“已移动”对象安全。
    • 作为一般规则,我同意。对于非生产代码,尤其是这种测试,我认为还可以。 (当然,对于像 std::unique_ptr 这样确实在移动后定义了内容的东西。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    相关资源
    最近更新 更多