正如希望从下面的代码中清楚看到的,我想拥有一组对象objectSet,每个对象包含str1和str2。该集合在str1上键入,并且不会添加任何在objectSet中已经存在str1的新对象,但是如果这个新对象具有不同的str2,我想跟踪我在str2Set中看到它的事实
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <set>
#include <map>
using namespace std;
class Object {
public:
string _str1;
string _str2;
set<string> _str2Set;
bool operator<(const Object& b) const {
return _str1 < b._str1;
}
};
int main(int argc, char *argv[]) {
set<Object> objectSet;
Object o;
o._str1 = "str1";
o._str2 = "str2";
pair< set<Object>::iterator, bool> o_ret = objectSet.insert(o);
if (o_ret.second == false) { // key exists
int temp = (*o_ret.first)._str2Set.size(); // this is apparently fine
(*o_ret.first)._str2Set.insert(o._str2); // this results in the error
}
return 0;
}
以下是编译器错误:
set_test.cpp:在函数'int main(int,char **)'中: set_test.cpp:31:错误:传递'const std :: set,std :: allocator&gt;,std :: less,std :: allocator&gt; &gt;,std :: allocator,std :: allocator&gt; &GT; &gt;''作为'std :: pair,_Compare,typename _Alloc :: rebind&lt; _Key&gt; :: other&gt; :: const_iterator,bool&gt;的'this'参数std :: set&lt; _Key,_Compare,_ Alloc&gt; :: insert(const _Key&amp;)[with _Key = std :: basic_string,std :: allocator&gt;,_Compare = std :: less,std :: allocator&gt; &gt;,_ Alloc = std :: allocator,std :: allocator&gt; &gt;]'丢弃限定符
我理解这与const有关,但我仍然无法确切地知道问题是什么或如何解决它。摆脱const并没有帮助。
作为替代方案,我尝试将我的对象存储在
中map<string,Object> objectSet;
奇怪的是,以下工作正常:
pair< map<string,Object>::iterator, bool> o_ret = objectSet.insert(pair<string,Object>(o._str1,o));
if (o_ret.second == false) { // key exists
o_ret.first->second._str2Set.insert(o._str2);
}
当然,这意味着我必须存储str1两次,我认为这是浪费。 感谢您的输入。
答案 0 :(得分:2)
错误表明(*o_ret.first)._str2Set
是const
个对象,因此您无法为其调用insert
方法。
并且它非常正确:因为您不允许修改std::set
中的对象(因为这可能会使容器的一致性无效),所以在解除引用{{{{}}时会得到const
限定对象1}}进入容器(好像它是iterator
)。
您还注意到它适用于const_iterator
,但这是因为您在那里修改了值,而不是密钥。请记住,在std::map
中,值是键,因此您无法对其进行修改。
答案 1 :(得分:2)
您的设计存在缺陷。您正在使用Object作为集合的键,但是您正在尝试修改集合的键。当然,您只修改了不影响它作为键使用的Object的部分,但编译器不知道这一点。你需要修改你的设计,你的第二个版本对我来说很好,我不担心存储两次字符串(通常,我不知道你的具体情况)。或者,您可以拆分对象,以便关键部分和值部分分开。最后,您可以将_str2set声明为可变。
答案 2 :(得分:1)
从set插件返回的迭代器有一个const迭代器,用于该对的第一个成员 - 你不应该修改你在集合中插入的对象,因为如果修改影响了命令会发生坏事。因此调用它的大小是可以的,因为这是一个const方法,但插入是因为它修改了集合中的对象。 地图有效,因为对象是值而不是键,因此您可以在不影响地图索引的情况下对其进行修改(在执行地图插入时复制字符串)。
如果要使用集合存储对象并避免额外的字符串副本,更新它的方法是从集合中删除对象(在删除之前制作副本),更新副本,以及然后重新插入副本。您无法在集合中更新它。我现在远离我的STL参考,所以我不能给你代码,但这是一般的想法。
答案 3 :(得分:1)
其他答案已经正确地说明了您的方法存在的问题,这是一个解决方案的想法。由于第一个字符串是您的密钥,因此将主数据结构更改为键入第一个字符串的std::map
,并将其余数据作为有效负载进行传输:
typedef std::pair< std::string, std::set<std::string> > Payload; // or make your own class
typedef std::map<std::string, Payload> Collection;
typedef Collection::value_type Data; // this is a std::pair<string, Payload>
// A little helper to create new data objects
Data make_data(std::string s1, std::string s2)
{
return Data(s1, Payload(s2, std::set<std::string>()));
}
Collection m;
Data x = make_data("mystr1", "mystr2");
std::pair<Collection::iterator, bool> res = m.insert(x);
if (res.second == false)
{
Payload & p = *res.first;
p.second.insert(x.second.first);
}
在某种意义上,第二个字符串有点多余,您可能可以重新设计它以完全取消它:而不是insert
使用find
,如果密钥已经存在,你将第二个字符串追加到集合中。
答案 4 :(得分:0)
在阅读了所有人的有用评论和学习方式之后,我想知道关于浅层常量和可变性的知识,我意识到只需存储指向_str2set的指针就能完成我想要的一切。我个人认为按照@john的建议宣布它是可变的很好,但也许有些人会发现ptr解决方案不那么令人讨厌。
class Object {
public:
string _str1;
string _str2;
set<string> * _str2Set;
bool operator<(const Object& b) const {
return _str1 < b._str1;
}
};
int main(int argc, char *argv[]) {
set<Object> objectSet;
Object o;
o._str1 = "str1";
o._str2 = "str2";
o._str2Set = new (set<string>);
pair< set<Object>::iterator, bool> o_ret = objectSet.insert(o);
if (o_ret.second == false) { // key exists
(*o_ret.first)._str2Set->insert(o._str2); // this results in the error
cout << (*o_ret.first)._str2Set->size() << endl;
}
return 0;
}