我应该在哪里放置用户定义类型的专用std :: hash

时间:2015-09-21 14:21:24

标签: c++11 hash unordered-set user-defined

我搜索了很多页面,我想我已经知道如何编写std :: hash。但我不知道该把它放在哪里。

此处提供了一个示例http://en.cppreference.com/w/cpp/utility/hash

但是,我在文件Instance中的名称空间ca中定义了我的类型instance_management.h。我想在另一个班级unordered_set<Instance>的同一个文件中使用InstanceManager。所以我写下面的代码:

namespace std
{
    template <> struct hash<ca::Instance>
    {
        size_t operator()(const ca::Instance & instance) const
        {
            std::size_t seed = 0;
            // Some hash value calculation here.
            return seed;
        }
    };
} // namespace std

但我应该把它放在哪里?我尝试了很多地方,但都失败了。

我正在使用visual studio 2013.我尝试将以前的代码放在某些位置,但都无法编译。

// location 1

namespace ca
{
    class Instance {...}
    class InstanceManager
    {
        // ... some other things.
        private unordered_set<Instance>;
    }
}

// location 2

3 个答案:

答案 0 :(得分:4)

有几种方法。

专业std::hash

在您的代码中,确保您的std::hash<Instance>专门化紧跟Instance类定义,然后使用使用它的unordered_set容器。

namespace ca
{
    class Instance {...};

}

namespaces std {

    template<> hash<Instance> { ... };

}

namespace ca {

    class InstanceManager
    {
        // ... some other things.
        private unordered_set<Instance>;
    }
}

一个缺点是,在将std::hash<ca::Instance>传递给其他函数时,您可以获得有趣的名称查找干扰。原因是ca的所有模板参数的关联命名空间(std::hash)可以在名称查找(ADL)期间使用。这样的错误有点罕见,但是如果它们发生则很难调试。

有关详细信息,请参阅this Q&A

将您的哈希值传递给unordered_set

struct MyInstanceHash { ... };

using MyUnorderedSet = std:unordered_set<Instance, MyInstanceHash>;

在这里,您只需将自己的哈希函数传递给容器即可。缺点是您必须明确键入自己的容器。

使用hash_append

但请注意,N3980标准提案目前正在等待审核。该提议采用了一种更优越的设计,它使用通用散列函数,通过其模板参数(实际散列算法)对任意字节流进行散列

template <class HashAlgorithm>
struct uhash
{
    using result_type = typename HashAlgorithm::result_type;

    template <class T>
    result_type
    operator()(T const& t) const noexcept
    {
        HashAlgorithm h;
        using std::hash_append;
        hash_append(h, t);
        return static_cast<result_type>(h);
    }
};

然后,用户定义的类X必须提供正确的hash_append,通过该类将其自身呈现为字节流,准备好由univeral hasher进行哈希处理。

class X
{
    std::tuple<short, unsigned char, unsigned char> date_;
    std::vector<std::pair<int, int>>                data_;

public:
    // ...
    friend bool operator==(X const& x, X const& y)
    {
        return std::tie(x.date_, x.data_) == std::tie(y.date_, y.data_);
    }

    // Hook into the system like this
    template <class HashAlgorithm>
    friend void hash_append(HashAlgorithm& h, X const& x) noexcept
    {
        using std::hash_append;
        hash_append(h, x.date_);
        hash_append(h, x.data_);
    }
}

有关详细信息,请参阅作者@HowardHinnant在CppCon14上的演示文稿(slidesvideo)。 <{3}}和author都有完整的源代码。

答案 1 :(得分:1)

不要专门化std::hash,而是编写自己的哈希函数对象(请参阅下面的Edge_Hash)并使用两个模板参数声明unordered_set

#include <unordered_set>
#include <functional>

namespace foo
{
    // an edge is a link between two nodes
    struct Edge
    {
        size_t src, dst;
    };

    // this is an example of symmetric hash (suitable for undirected graphs)
    struct Edge_Hash
    {
        inline size_t operator() ( const Edge& e ) const
        {
            static std::hash<size_t> H;
            return H(e.src) ^ H(e.dst);
        }
    };

    // this keeps all edges in a set based on their hash value
    struct Edge_Set
    {
        // I think this is what you're trying to do?
        std::unordered_set<Edge,Edge_Hash> edges;
    };
}

int main()
{
    foo::Edge_Set e;
}

相关帖子是,例如:

答案 2 :(得分:0)

感谢大家。

我找到了原因并以某种方式解决了问题:当我定义InstanceHash时,visual studio接受了instances_。由于我正在将set的使用更改为unordered_set,因此当我尝试获取InstanceHash时忘记指定const_iterator,所以这次编译器尝试使用{ {1}}事情并失败了。但是编译器没有使用std::hash<>找到该行,所以当我定义const_iterator时,我错误地认为它没有接受InstanceHash

我还尝试将instances_专门用于类Instance。但是,此专门化至少需要声明类std::hash<>及其某些成员函数来计算哈希值。在此专业化之后,类ca::Instance的定义将使用它。

我现在通常将几乎所有类和成员函数的声明和实现放在一起。所以,我需要做的是将ca::InstanceManage命名空间范围拆分为2个部分并将ca放在中间。