我正在尝试使用以下自定义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_0boostcontainer_hashextensions.hpp:305: error: C2665: 'boost::hash_value': none of the 3 overloads could convert all the argument types
C:boost_1_77_0boost/container_hash/hash.hpp(550): note: could be 'size_t boost::hash_value(const std::error_condition &)'
C:boost_1_77_0boost/container_hash/hash.hpp(543): note: or 'size_t boost::hash_value(const std::error_code &)'
C:boost_1_77_0boost/container_hash/hash.hpp(536): note: or 'size_t boost::hash_value(std::type_index)'
C:boost_1_77_0boost/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<>
可能使用了使用hash_value
重载的boost::hash_combine
,而它没有用于QColor
的重载,这可能是一个问题,因此我建议您通过将别名从类定义中移出来为std::hash<Pair>
创建一个专用化,然后直接在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<Pair>
(:
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&)
提供过载,而不是为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;
};