【问题标题】:an STL set of custom objects, each containing an STL set一组自定义对象的 STL 集,每个对象都包含一个 STL 集
【发布时间】:2011-08-12 06:22:57
【问题描述】:

希望从下面的代码中可以清楚地看出,我想要一组对象 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 >, std::less, std::allocator > >, std::allocator, std::allocator > > >' as 'this ' 'std::pair, _Compare, typename _Alloc::rebind<_key>::other>::const_iterator, bool> std::set<_key _compare _alloc>::insert(const _Key&) 的参数 [with _Key = std::basic_string, std::allocator >, _Compare = std::less, std::allocator > >, _Alloc = std::allocator, std::allocator >]' 丢弃限定符

我知道这与 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 是什么?
  • 不明白算法解决了什么问题。
  • 好吧,假设对象是人,键入唯一的姓氏。但我也想跟踪每个不同的名字(如果不止一个),而不需要重复存储姓氏

标签: c++ map iterator set mutable


【解决方案1】:

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

【讨论】:

  • 我尝试将 _str2set 声明为可变,它按我想要的方式工作。我对 mutable 了解不多,所以我在这里和其他地方查找了它。看起来有很多关于它被不当使用的咆哮。在我看来,由于设置键没有受到影响,所以它很干净,但我很好奇其他大师对此有什么看法。请注意我上面关于记忆很重要的说明。此外,在我的情况下,在其余代码中拆分对象没有多大意义。
  • 你用 mutable 那样作弊。可变的适用于实现可能必须修改对象的情况,即使对于外部用户该对象似乎没有更改。例如,缓存数据库中的值的类。类上的 getter 将是 const (当然),但也必须使用缓存值更新对象。您的情况并非如此,您正在使用 mutable 来解决标准库并不像您希望的那样聪明的事实。但我不是狂热者,我认为所有这些情况都是判断要求。
【解决方案2】:

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

这是完全正确的:由于不允许修改 std::set 中的对象(因为这可能会使容器的一致性无效),因此在将 iterator 取消引用到容器中时,您会得到一个 const 限定对象(好像是const_iterator)。

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

【讨论】:

    【解决方案3】:

    您从集合插入返回的迭代器有一个用于该对的第一个成员的 const 迭代器 - 您不应该修改您在集合中插入的对象,因为如果修改影响了顺序,就会发生不好的事情.所以在它上面调用 size 是可以的,因为那是一个 const 方法,但是 insert 因为它修改了集合中的对象而被淘汰了。 映射有效,因为 Object 是值,而不是键,因此您可以在不影响映射索引的情况下对其进行修改(插入映射时会复制字符串)。

    如果你想使用集合来存储你的对象并避免额外的字符串副本,更新它的方法是从集合中删除对象(在删除之前制作副本),更新副本,然后然后重新插入副本。当它在集合中时,您无法对其进行更新。我目前没有我的 STL 参考,所以我不能给你代码,但这是要遵循的一般想法。

    【讨论】:

      【解决方案4】:

      其他答案已经正确说明了您的方法存在的问题,这是一个解决方案的想法。由于第一个字符串是您的键,因此将您的主要数据结构更改为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,如果密钥已经存在,则附加第二个字符串到集合。

      【讨论】:

        【解决方案5】:

        在阅读了每个人的有用 cmets 并学习了比我想知道的更多关于浅常量和可变的方法后,我意识到我可以通过存储指向 _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;
        }
        

        【讨论】:

          猜你喜欢
          • 2012-05-19
          • 1970-01-01
          • 2020-09-03
          • 2021-06-05
          • 2020-12-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多