2017-04-17 76 views
1

我有这个类的成员变量是set<pair<string,map<string,int> > > setLabelsWords;这是有点复杂,但忍受着我。在同一类的成员函数,我有以下代码:插入不会永久更改地图?

pair<map<string,int>::iterator,bool> ret; 

for (auto j:setLabelsWords) { 
    if (j.first == label) { 
    for (auto k:words) { 
     ret = j.second.insert(make_pair(k,1)); 
     if (ret.second == false) { 
     j.second[k]++; 
     } 
    } 
    } 
} 

“字”是一组字符串和“标签”是一个字符串。所以基本上它应该插入“k”(一个字符串),如果k已经在映射中,它会将int递增1。问题是它一直运行,直到最外层的for循环结束。如果我在最后一个括号之前打印j.second的大小,它会给我一个我期望的大小为13的大小,但是在最后一个括号之外,大小可以回到7,这就是该代码在映射之前的最初大小块运行。我非常困惑,为什么会发生这种情况,任何帮助都将非常感谢。

回答

4
for (auto j:setLabelsWords) { 

此迭代容器由值。这是同样的事情,如果你这样做:

class Whatever { /* something in here */ }; 

void changeWhatever(Whatever w); 

// ... 

{ 
    Whatever w; 

    changewhatever(w); 
} 

无论changewhatever确实给w,任何修改作出,是的w副本所做的,因为它被按值传递,给函数。

为了正确地更新你的容器,必须参考迭代:

for (auto &j:setLabelsWords) { 
+0

啊我明白了!后续问题q:编译器现在抛出一个错误,因为由于某种原因它将j.second视为一个常量,所以我无法用insert或[]修改它。有没有解决的办法? – newtothis

+0

看起来你的容器是'std :: set'。一组中的值不能修改。毕竟,一个集合命令它的内容,并且修改集合中的值可能会改变其内容的相对顺序,因此设置的值是const。使用不同的容器。关于容器的好处是它们的统一方法,所以你的代码不会需要任何改变。只需切换容器。 –

2
for (auto j:setLabelsWords) { 

这产生了复制每个元件的。您在j上执行的所有操作都会影响该副本,而不是setLabelsWords中的原始元素。

通常情况下,你只用一个参考

for (auto&& j:setLabelsWords) { 

然而,由于std::set的性质,这会不会让你走得很远,因为std::set的元素不能被随意修改通过迭代器或对元素的引用,因为这将允许您创建一个包含重复项的集合。编译器不会允许。

这里是一个务实的解决方案:使用std::vector代替std::set

std::vector<std::pair<std::string, std::map<std::string,int>>> setLabelsWords; 

然后,您将能够使用参考方法进行了说明。


如果您需要std::set小号元素的独特性,后来排序功能,您可以在std::vectorstd::vector适用std::sort和/或std::unique,或创建一个新std::set“。

+0

很肯定这只是一个'&',如果你想有一个参考。 – melpomene

+1

@melpomene:在这种情况下,它是等价的,但如果代码改变,'&&'自动转换为'const&'。在基于范围的'for'循环中使用'auto &&'通常被认为是很好的做法。 –

+0

@ChristianHackl当我改变它,它变成了一个常量,现在我不能修改地图(插入或[])...有没有办法解决这个问题? – newtothis