Boost:当 pair 包含自定义类型时,如何将 boost::hash 与 std::pair 一起使用?

Boost: how to use `boost::hash` with `std::pair`, when pair contains a custom type?

我正在尝试使用以下自定义 unordered_map

using pair = std::pair<char, QColor>;
using cache = std::unordered_map<pair, QPixmap, boost::hash<pair>>;
cache _cache;

我为QColor定义了哈希函数如下

template<>
struct std::hash<QColor>
{
    std::size_t operator()(const QColor &color) const noexcept
    {
        return std::hash<QRgb>{}(color.rgb());
    }
};

但是无论我把它放在哪里,无论是头文件还是源文件,我都会从 boost

得到一个详细的编译时错误
C:\boost_1_77_0\boost\container_hash\extensions.hpp:305: error: C2665: 'boost::hash_value': none of the 3 overloads could convert all the argument types
C:\boost_1_77_0\boost/container_hash/hash.hpp(550): note: could be 'size_t boost::hash_value(const std::error_condition &)'
C:\boost_1_77_0\boost/container_hash/hash.hpp(543): note: or       'size_t boost::hash_value(const std::error_code &)'
C:\boost_1_77_0\boost/container_hash/hash.hpp(536): note: or       'size_t boost::hash_value(std::type_index)'
C:\boost_1_77_0\boost/container_hash/extensions.hpp(305): note: while trying to match the argument list '(const T)'

这是最后一条消息。我认为 boost 的 pair 散列函数看不到我定义的散列函数。我需要在 boost 的命名空间中定义它吗?一般来说,定义特定版本模板的规则是什么?为什么只能在头文件中定义模板的规则在这里不适用?

UPD:我的项目结构如下

// foo.h
#include <QtWidgets>
#include <boost/functional/hash.hpp>

#include <unordered_map>

template<>
struct std::hash<QColor>
{
    std::size_t operator()(const QColor &color) const noexcept
    {
        return std::hash<QRgb>{}(color.rgb());
    }
};

class Foo
{
private:
    using Pair = std::pair<char, QColor>;
    const QPixmap &getPixmapForPair(Pair c);

    using CharsCache = std::unordered_map<Pair, QPixmap, boost::hash<Pair>>;
    CharsCache _cache;
}
// foo.cpp
const QPixmap &Foo::getPixmapForPair(Pair c)
{
    auto it = _cache.find(c);
    if (it != _cache.end())
        return it->second;
}

过于简单化,但传达了总体思路。

boost::hash<> 可能使用 boost::hash_combine ,它使用 hash_value 重载并且它没有用于 QColor 的重载,这可能是一个问题所以我建议你创建一个通过将别名从 class 定义中移出,然后在 operator():

中直接使用 boost::hash_combine 来专门化 std::hash<Pair>
using Pair = std::pair<char, QColor>;

namespace std {
    template<>
    struct hash<Pair> {
        std::size_t operator()(const Pair &p) const noexcept {
            std::size_t seed = 0;
            boost::hash_combine(seed, p.first);
            boost::hash_combine(seed, p.second.rgb());
            return seed;
        }
    };
} // namespace std

您可能会 std::size_t seed = p.first; 而不是使用 0 进行初始化,然后再调用 hash_combine

然后您可以在创建地图时使用默认哈希器 (std::hash<Pair>):

class Foo {
private:
    const QPixmap &getPixmapForPair(const Pair &c) const;

    using CharsCache = std::unordered_map<Pair, QPixmap>;
    CharsCache _cache;
};

注意函数必须return一个值。我建议你在找不到匹配项时抛出异常:

const QPixmap& Foo::getPixmapForPair(const Pair &c) const {
    auto it = _cache.find(c);
    if (it != _cache.end()) return it->second;
    throw std::runtime_error("getPixmapForPair"); // must return a value
}

另一种选择是为 hash_value(const QColor&) 提供重载,而不是为 std::hash<Pair> 提供特化:

std::size_t hash_value(const QColor& c) {
    return std::hash<QRgb>{}(c.rgb());
}

class Foo {
private:
    using Pair = std::pair<char, QColor>;
    const QPixmap& getPixmapForPair(const Pair& p) const;

    using CharsCache = std::unordered_map<Pair, QPixmap, boost::hash<Pair>>;
    CharsCache _cache;
};