unordered_set使用值对象地址的哈希值

时间:2013-10-13 17:03:59

标签: c++ c++11 hash c++-standard-library

我有一个类需要有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

3 个答案:

答案 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