我想练习线程,因此我创建了一个小解析器。我希望使线程版本尽可能快,因此我想避免使用移动语义进行复制。但我无法理解如何将移动语义和解析结合起来。 我的循环看起来与此类似:
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
最后一个让我困惑。它不会移动值,但也不会在每次迭代时复制它。
我明白,为什么它不移动 - 因为变量词将在下一次迭代中使用,并且它的生命周期更长。
但是为什么它复制得这么混乱?
没有什么奇怪的事情发生。
拳头案例:
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
。