一组STL自定义对象,每个对象包含一个STL集

时间:2011-08-12 06:22:58

标签: c++ map iterator set mutable

正如希望从下面的代码中清楚看到的,我想拥有一组对象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两次,我认为这是浪费。 感谢您的输入。

5 个答案:

答案 0 :(得分:2)

错误表明(*o_ret.first)._str2Setconst个对象,因此您无法为其调用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;
}
相关问题