【问题标题】:Boost: how to use `boost::hash` with `std::pair`, when pair contains a custom type?Boost:当pair包含自定义类型时,如何将`boost::hash`与`std::pair`一起使用?
【发布时间】:2022-01-07 23:01:29
【问题描述】:

我正在尝试使用以下自定义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;
}

非常简单,但传达了总体思路。

【问题讨论】:

  • 你能试试this吗?我没有可用的 MSVC + Qt + boost,所以我无法自己验证它,但是当模拟 Qt 类型时,它在 MSVC 中对我有用。如果我删除模拟它在g++clang++ 中工作正常@
  • @TedLyngmo 奇怪,但它确实适用于单个源文件中的模拟和真实 Qt 类型 - 您提供的那个。但是当涉及到结构更复杂的项目时 - 我收到了问题中的错误。我会用我的项目结构解释来更新我的问题。

标签: c++ qt templates boost


【解决方案1】:

boost::hash&lt;&gt; 可能使用boost::hash_combine,它使用hash_value 重载,而QColor 没有重载,这可能是个问题,所以我建议您通过移出别名来为std::hash&lt;Pair&gt; 创建一个专业化的类定义,然后直接在你的operator() 中使用boost::hash_combine

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&lt;Pair&gt;):

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

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

请注意,函数必须返回一个值。如果找不到匹配项,我建议您抛出异常:

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&amp;) 提供重载,而不是为std::hash&lt;Pair&gt; 提供特化:

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;
};

【讨论】:

  • hash 的特定定义在哪里更好?我觉得唯一正确的位置是在标题中的类定义之前。但为什么?是的,对不起,我确实从使用该对的函数中返回了值,只是在简化它时忘记包含代码。
  • @sauce std::hash&lt;QColor&gt;的定义?在你使用它之前,我会说它是一个好地方。请注意,我在答案的底部添加了一个更简单的解决方案。
  • "我确实从函数返回值" - 是的,但前提是你找到匹配项。
  • 我的意思是有一个我没有提供的代码。
猜你喜欢
  • 1970-01-01
  • 2013-05-06
  • 2017-12-27
  • 1970-01-01
  • 2011-05-05
  • 2012-12-08
  • 1970-01-01
  • 2012-04-27
  • 1970-01-01
相关资源
最近更新 更多