私は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;
}
これを行う簡単な方法はありますか?または、要素が既に存在するかどうかを確認する必要がありますか?その場合、それを削除し、値を追加して再挿入しますか?
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
の値が論理的に定数のFooで変化する可能性があることを意味します。つまり、他の比較演算子などに影響を与えるべきではありません。
または、挿入と位置を使用する場合は、O(1)追加の時間(アクセスと変更に比べて) 直前 ヒントとして古いものの直後。
何かのようなもの:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
set
内のアイテムの定数を回避することでこの問題を解決しようとしないでください。代わりに、モデリングしているキーと値の関係を既に表現し、既存の要素を簡単に更新できるmap
を使用してください。
val
を次のように変更可能にします。
mutable int val;
val
がconstである場合でも、foo
を変更/変更/変更できます。
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
ちなみに、コードから、p.second
がtrueの場合、値は既にセットに存在しているため、関連する値を更新すると考えるようです。あなたはそれを間違えたと思う。実際、それは逆です。 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
}
}
しかし、これは多すぎませんか?パフォーマンスは良くありません。以前のものは、より簡潔で非常に高性能です。