我有一个类需要有std::unordered_set
来保存不可复制的,不可移动的实体对象,并且其哈希函数哈希实例的地址。如下所示:
class A
{
public:
A();
A(const A&) = delete;
A(A&&) = delete;
void operator=(const A&) = delete;
void operator=(A&&) = delete;
bool operator==(const A& other) { return this == &other; }
};
template<>
struct std::hash<A>
{
size_t operator()(const A& obj) const
{
return std::hash<A*>()(&obj);
}
};
class B
{
private:
std::unordered_set<A> entities;
};
如果始终使用emplace()
代替insert()
,以这种方式使用unordered_set
是否安全?标准是否指定实现在构造后不能移动节点对象?
如果A
可移动怎么办?是否可以保证在集合所拥有的对象上调用哈希函数,或者由于标准库更喜欢将所有内容都视为值对象,是否允许在分配存储之前对insert
ed对象进行哈希处理对吗?
作为最后的想法,我知道我可以使用std::unordered_set<std::unique_ptr<A>>
解决所有这些问题,但我想为A
对象使用自定义分配器,我不想覆盖new
的{{1}}和delete
。
答案 0 :(得分:2)
使用对象的地址作为哈希几乎可以保证您不会找到该对象,除非您已经拥有指向该对象的指针而不是通过哈希迭代。您需要提出一种不同的方法来从您的对象获取哈希值。也就是说,一旦在哈希内部构建,对象的地址就不会改变。
答案 1 :(得分:1)
我仍然认为你最好使用std::list
。考虑:
#include <iostream>
#include <list>
class A
{
public:
int i_;
A(int i) : i_(i) {}
A(const A&) = delete;
A(A&&) = delete;
void operator=(const A&) = delete;
void operator=(A&&) = delete;
};
int main()
{
std::list< A > l;
// inserting elements
auto it1 = l.emplace( l.end(), 1 ); // note: complexity is O(1)
auto it2 = l.emplace( l.end(), 2 );
auto it3 = l.emplace( l.end(), 3 );
auto it4 = l.emplace( l.end(), 4 );
// deleting an element by iterator
l.erase( it2 ); // note: complexity is O(1)
// note: it2 is now invalid
// accessing element by iterator
it3->i_ = 42;
for( const auto& e : l ) {
std::cout << e.i_ << std::endl;
}
// silence compiler warnings
(void)it1;
(void)it4;
}
在上面,所有用例都应该有效实现。您可以避免计算哈希和哈希映射的开销。它比基于哈希的方法更有效,对于列表,这两个操作都是O(1),并且实现时更加轻松。存储迭代器与直接存储指向元素的指针没什么不同。
此外,保证这适用于不可复制和不可移动的类型。请参阅std::list::emplace
的文档。
答案 2 :(得分:0)
正如Dietmar所提到的,一旦构建,价值的地址就不会改变。至于问题的第二部分,标准似乎不仅允许,而且要求实现通过引用调用传递给insert()
的对象上的hash / equal_to仿函数,而不是首先要求构造节点并调用该对象上的函数:
从23.2.5表103 - 无序关联容器要求
pair<iterator, bool> a_uniq.insert(t)
效果:当且仅当容器中没有元素且密钥等效于
t
的键时才插入t
。