如何更新std :: set的现有元素?

时间:2011-09-07 20:55:18

标签: c++ stl set

我有std::set<Foo>,我想更新一些值 其中的现有元素。请注意,我正在更新的值不会更改集合中的顺序:

#include <iostream>
#include <set>
#include <utility>

struct Foo {
  Foo(int i, int j) : id(i), val(j) {}
  int id;
  int val;
  bool operator<(const Foo& other) const {
    return id < other.id;
  }
};

typedef std::set<Foo> Set;

void update(Set& s, Foo f) {
  std::pair<Set::iterator, bool> p = s.insert(f);
  bool alreadyThere = p.second;
  if (alreadyThere)
    p.first->val += f.val; // error: assignment of data-member
                           // ‘Foo::val’ in read-only structure
}

int main(int argc, char** argv){
  Set s;
  update(s, Foo(1, 10));
  update(s, Foo(1, 5));
  // Now there should be one Foo object with val==15 in the set.                                                                
  return 0;
}

有没有简洁的方法来做到这一点?或者我是否必须检查元素是否已存在,如果是,请将其删除,添加值并重新插入?

4 个答案:

答案 0 :(得分:51)

由于val未参与比较,因此可以将其声明为mutable

struct Foo {
  Foo(int i, int j) : id(i), val(j) {}
  int id;
  mutable int val;
  bool operator<(const Foo& other) const {
    return id < other.id;
  }
};

这意味着val的值可能会在逻辑const Foo中发生变化,这意味着它不应该影响其他比较运算符等。

或者你可以删除和插入,如果插入使用之前的位置就在旧的那个之后作为提示,那么需要花费O(1)额外的时间(与访问和修改相比)。 / p>

类似的东西:

bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
    Set::iterator hint = p.first;
    hint++;
    s.erase(p.first);
    s.insert(hint, f);
}

答案 1 :(得分:25)

不要试图通过解决set中项目的常量来解决这个问题。相反,为什么不使用map,它已经表达了您正在建模的键值关系,并提供了更新现有元素的简单方法。

答案 2 :(得分:5)

val变为:

mutable int val;

现在你可以改变/修改/变异val即使foo是const:

void f(const Foo & foo)
{
     foo.val = 10;  //ok
     foo.id  = 11;  //compilation error - id is not mutable.
}

顺便说一句,从您的代码中,您似乎认为如果p.second为真,则该值已存在于集合中,因此您更新了关联的值。我想,你弄错了。实际上它是其他方式。 cpluscplus的doc表示,

  

如果插入了新元素,则对中的pair :: second元素设置为true;如果存在具有相同值的元素,则设置为false。

这在我看来是正确的。


但是,如果您使用std::map,您的解决方案将非常简单:

void update(std::map<int,int> & m, std::pair<int,int> value) 
{
    m[value.first] += value.second;
}

这段代码有什么作用?如果映射中不存在键,则m[value.first]创建新条目,新条目的值为默认值int,即零。因此,value.second添加到zero。或者,如果密钥存在,则只需向其添加value.second即可。也就是说,上面的代码等同于:

void update(std::map<int,int> & m, std::pair<int,int> value) 
{
    std::map<int,int>::iterator it = m.find(value);
    if ( it != m.end()) //found or not?
           it.second += value; //add if found
    else
    {
           m.insert(value); //insert if not found
    }
}

但这太过分了,不是吗?它的表现并不好。较早的一个更简洁,效率更高。

答案 3 :(得分:-1)

你可以使用MAP如果你有KEY,你可以非常快速地访问你的元素。在这种情况下,我认为使用MAP将是实现最快速度的更好方法。 STD :: MAP