我有一些指向某些对象的std::unordered_set
指针。该集具有自定义散列和等价函数,s.t。即使对象在“所有成员都相等”的意义上不相等,对象也可以在集合上相等。
现在我要插入一个新对象。如果集合中已经存在等效对象,那么当且仅当对象的“其他”成员(即,不是散列/等式检查的一部分)上的某些条件为真时,才希望替换旧对象。
如果我决定更换物体,我想知道如何最有效地做到这一点。我觉得整个过程应该可以通过单个hashmap查找来完成。我目前最好的方法是:
set.insert(new_object)
。如果实际插入对象,我们就完成了。 (这会花费我们一次哈希映射查找。)new_object
的任何内容。这是棘手的部分。看起来我做不到new_object
。我目前可以理解的最好的是*the_iterator = new_object
- 但这需要对插入进行新的hashmap查找,甚至可能导致重新散列。两者都没有必要。下面是一个完整的例子,展示了我正在尝试做的事情。由于自定义哈希/等式结构,它有点冗长,抱歉。
set.erase(the_iterator); set.insert(new_object);
有人看到如何通过一组查找来完成此操作吗?我可以使用C ++ 11或14功能。谢谢!
答案 0 :(得分:3)
不,这是不可能的,按设计。
对集合中包含的元素的所有访问都是const
限定的,因此集合可以维护必要的结构,以便能够在O(1)时间内进行搜索。如果你可以修改元素,它们可能位于错误的位置。
您可以insert
将my_set
(或直接在C ++ 17 merge
中)的内容转换为新的(单个元素)集然后交换它们,但这是一个O(N)操作。
decltype(my_set) temp{ { foo } };
temp.insert(my_set.begin(), my_set.end());
std::swap(my_set, temp);
// my_set now has foo, temp has replaced element or is empty
或者,如果您只关心foo->other_value
放入集合的值,请注意您可以通过const
指针修改对象。
auto ptr = *my_set.insert(foo).first;
ptr->other_value = foo->other_value;
assert(ptr->set_value == foo->set_value);
// ptr is "Foo * const", not "Foo const *"
答案 1 :(得分:0)
这样的事情对你有用吗?可能不是最好的设计,您可以使用成员class MySet
对其进行优化(可能有一个包装器replace_if_lower
)。我们的想法是使用shared_ptr
为可能不安全的操作添加一点安全性。
#include <iostream>
#include <unordered_set>
#include <memory>
struct Foo
{
int set_value;
int other_value;
Foo(int _s, int _o)
:
set_value(_s),
other_value(_o)
{}
friend std::ostream& operator << (std::ostream& _s, const Foo& _f)
{
return _s << _f.set_value << ", " << _f.other_value;
}
};
using FooPtr = std::shared_ptr<Foo>;
struct SetEqual {
bool operator()(const FooPtr& lhs, const FooPtr& rhs) const noexcept
{
return lhs->set_value == rhs->set_value;
}
};
struct SetHasher {
size_t operator()(const FooPtr& foo) const noexcept
{
std::hash<int> hasher;
return hasher(foo->set_value);
}
};
std::unordered_set<FooPtr, SetHasher, SetEqual> my_set;
void replace_if_lower(FooPtr& foo)
{
auto insertion_result = my_set.insert(foo);
bool was_inserted = insertion_result.second;
auto insertion_it = insertion_result.first;
if (!was_inserted)
{
// Replace iff the other value is lower
if (foo->other_value < (*insertion_it)->other_value)
{
// This is what I would like to do:
// *insertion_it = foo;
// Swap the pointer contents:
const_cast<FooPtr&>(*insertion_it).swap(foo);
}
}
}
int main()
{
my_set.emplace(std::make_shared<Foo>(1,1));
my_set.emplace(std::make_shared<Foo>(2,1));
my_set.emplace(std::make_shared<Foo>(3,1));
std::cout << "Foo set:\n";
for (const auto& f : my_set)
{
std::cout << f << ": " << *f << "\n";
}
std::cout << "\n";
FooPtr f30(std::make_shared<Foo>(3,0));
replace_if_lower(f30);
std::cout << "Foo set:\n";
for (const auto& f : my_set)
{
std::cout << f << ": " << *f << "\n";
}
std::cout << "\n";
FooPtr f32(std::make_shared<Foo>(3,2));
replace_if_lower(f32);
std::cout << "Foo set:\n";
for (const auto& f : my_set)
{
std::cout << f << ": " << *f << "\n";
}
std::cout << "\n";
return 0;
}
打印
Foo set:
0x563e834eec70: 3, 1
0x563e834eec90: 2, 1
0x563e834eec30: 1, 1
Foo set:
0x563e834efd30: 3, 0
0x563e834eec90: 2, 1
0x563e834eec30: 1, 1
Foo set:
0x563e834efd30: 3, 0
0x563e834eec90: 2, 1
0x563e834eec30: 1, 1