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

an STL set of custom objects, each containing an STL set

本文关键字:STL 集合 对象 包含一 自定义      更新时间:2023-10-16

希望从下面的代码中可以清楚地看到,我希望有一组对象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: In function ' int main(int, char**) ':set_test.cpp:31:错误:传递' const std::set, std::allocator>, std::allocator>>, std::allocator>>, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator, std::allocator>> '作为' std::pair, _Compare, _Alloc>::insert(const _Key&) [with _Key = std::basic_string, std::allocator>>, _Compare, _Alloc>::insert(const _Key&) '的' this '参数丢弃限定符

我知道这与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两次,我认为这很浪费。谢谢你的建议。

你的设计有缺陷。您正在使用Object作为集合的键,但随后您试图修改集合的键。当然,你只是修改了Object中不影响它作为键使用的部分,但是编译器并不知道这一点。你需要修改你的设计,你的第二个版本对我来说很好,我不会担心存储字符串两次(通常,我不知道你的具体情况)。或者你可以拆分你的对象,这样键部分和值部分就分开了。最后,您可以将_str2set声明为可变的

错误提示(*o_ret.first)._str2Setconst对象,因此不能为其调用insert方法。

这是完全正确的:因为您不允许修改std::set中的对象(因为这可能会使容器的一致性失效),所以当将iterator解引用到容器中时(就好像它是const_iterator),您将获得const合格的对象。

您还注意到它适用于std::map,但这是因为您正在修改那里的值,而不是键。请记住,在std::set中,值是键,所以您不能修改它。

从set插入操作返回的迭代器有一个const迭代器,用于对象对的第一个成员——你不应该修改插入到set中的对象,因为如果修改影响了顺序,就会发生不好的事情。调用size是可以的,因为这是const方法,但insert不行因为它修改了集合中的对象。map之所以有效,是因为Object是值,而不是键,所以你可以修改它而不影响map的索引(当你插入map时,字符串会被复制)。

如果你想用一个集合来存储你的对象,并避免额外的string拷贝,更新它的方法是从集合中删除对象(在删除它之前做一个副本),更新副本,然后重新插入副本。当它在集合中时,你不能更新它。我现在离开了STL参考,所以我不能给你代码,但这是一般的想法。

其他答案已经正确地说明了您的方法的问题,这里有一个解决方案的想法。由于第一个字符串是键,因此将主数据结构更改为第一个字符串上的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);
}

在某种意义上,第二个字符串有点多余,您可以重新设计它以完全消除它:使用find代替insert,如果键已经存在,则将第二个字符串附加到集合中。

在阅读了每个人的有用评论并学习了比我想象的更多的方法我想知道的关于浅层constness和mutable,我意识到我可以简单地通过存储一个指向_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;
}