如何在循环中使用移动语义并避免复制?

问题描述 投票:0回答:1

我想练习线程,因此我创建了一个小解析器。我希望使线程版本尽可能快,因此我想避免使用移动语义进行复制。但我无法理解如何将移动语义和解析结合起来。 我的循环看起来与此类似:

string word = "";
string s = "pip pip pop som"
unordered_map<string, int> dic;

for (char c : s) {
    if ('a' <= c && c <= 'z') {  // lower case 
        word.push_back(c);
    }
    else if ('A' <= c && c <= 'Z') {  // upper case
        word.push_back(c + 32);
    }
    else if (!word.empty()) {  // word ended
        ++dic[word]; // I would like to avoid copying here
        word.clear();
    }
}
if (!word.empty()) {
    ++dic[word];
}

dic 将包含:

“索姆”1
“点”2
“流行”1

我尝试

std::move(word)
进入
dic
,但没有成功。 您能建议一下如何避免在这里复制并使用
std::move
吗? 遗憾的是,我无法在循环内创建
word
变量。

当我为这个问题做了一个最小的例子时,它变得更加奇怪:

#include <iostream>
#include <utility>
#include <map>
#include <ranges>

int main()
{
    using namespace std;

    map<string, int> m;
    string word;

    for (auto i : views::iota(0, 10)) {
        word.push_back('a');
        ++m[word];  // I would like to avoid copying here
        word.clear();
    }

    for (auto [word, count] : m) {
        cout << word << ' ' << count << endl;
    }

    return 0;
}

根据您在第一个循环中编写的内容,输出会有所不同。

word.push_back('a');
++m[word];
word.clear();

输出:

一个10

word.push_back('a');
++m[word];

输出:

一个1
AA 1
啊啊1
啊啊啊1
啊啊啊1
啊啊啊1
啊啊啊1
啊啊啊啊1
啊啊啊啊1
啊啊啊啊1

word.push_back('a');
++m[move(word)];

输出:

4
AA 3
啊啊2
啊啊啊1

最后一个让我困惑。它不会移动值,但也不会在每次迭代时复制它。

我明白,为什么它不移动 - 因为变量词将在下一次迭代中使用,并且它的生命周期更长。

但是为什么它复制得这么混乱?

c++ loops move-semantics stdmove
1个回答
0
投票

没有什么奇怪的事情发生。

拳头案例:

word.push_back('a');
++m[word];
word.clear();

每次迭代,您都会向字符串添加

'a'
,增加字符串
"a"
的计数,清除字符串以使用空
word
开始下一次迭代。你这样做10次。因此,在循环之后,
"a"
的计数为
10

下一个案例:

word.push_back('a');
++m[word];

每次迭代,您都会向

'a'
添加一个
word
,并增加当前
word
的计数。你这样做10次。循环结束后,您增加了 10 个不同单词的计数,每个字符都比前一个字符长。

最后一个案例:

word.push_back('a');
++m[move(word)];

std::string
的移动构造函数使移动的字符串处于有效但未指定的状态(了解更多这里)。原则上,
word
的内容可以是您从其中移出后的任何内容。

您看到的是短字符串优化的效果。短字符串直接存储在

std::string
中。不进行动态分配。因此没有什么可以移动的。移动短字符串实际上就是复制它。仅当字符串太长而无法使用短字符串优化时,移动后的字符串为空。这些都不能指望。如上所述,移出的字符串处于未指定状态。当您仍然需要该值时,您不应该从它移动,并且当您需要从空字符串移动时,您必须调用
clear

© www.soinside.com 2019 - 2024. All rights reserved.