使用 QString 作为 std::unordered_map 中的键
Using QString as the key in std::unordered_map
我正在尝试使用 QString
作为 std::unordered_map
中的键,但是我收到错误消息:
error C2280: 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': attempting to reference a deleted function
我无法切换到 QHash
,因为地图的值类型是不可复制的。有什么办法可以做到这一点吗?
问题是没有 std::hash<QString>()
专业化。根据 dbj2 算法定义您自己的具有相当好的性能的算法很容易:
#include <QString>
#include <unordered_map>
namespace std
{
template<> struct hash<QString>
{
std::size_t operator()(const QString& s) const noexcept
{
const QChar* str = s.data();
std::size_t hash = 5381;
for (int i = 0; i < s.size(); ++i)
hash = ((hash << 5) + hash) + ((str->row() << 8) | (str++)->cell());
return hash;
}
};
}
将其包含在 std::unordered_map
中使用 QString
的文件中,错误消失。
将 hash
实现放在 header 中,并确保在使用地图的任何地方都包含 header。
转发到 qHash
的简单实现应该就足够了:
#include <QHash>
#include <QString>
#include <functional>
namespace std {
template<> struct hash<QString> {
std::size_t operator()(const QString& s) const noexcept {
return (size_t) qHash(s);
}
};
}
尽管在常见的 64 位平台上 std::size_t
大于 unsigned int
,因此哈希不会在其整个长度上发生变化 - 这不是问题。该标准对 std::hash
实施没有这样的要求。
但我们不要忘记,修改 std
命名空间中的任何内容通常是未定义的行为,是不必要的。
长话短说:
您可以特化某些 type 和 variable 模板,前提是特化至少依赖于一种 user-defined 类型。您不能完全专注于 built-in 或 C++ 库类型。
参考Extending the namespace std。在那里,我们读到:
It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below.
主要是允许在 std
命名空间中特化某些 类型 :
It is allowed to add template specializations for any standard library class template to the namespace std only if the declaration depends on at least one program-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited.
请注意,合法的是类型,即类。函数和成员函数?从来没有:
It is undefined behavior to declare a full specialization of any standard library function template, [... or] member function of a standard library class template, [... or] member function template of a standard library class or class template.
另一个有限的例外是变量模板:
It is undefined behavior to declare a full or partial specialization of any standard library variable template, except where explicitly allowed.
在所有情况下都强调我的。一如既往,还有 details that one should get acquainted with.
似乎在较新版本的 Qt 中,std::hash 是为 QString 定义的,因此您可以直接将其与 std::unordered_map 一起使用。 (它适用于我机器上的 Qt 5.14。)
我正在尝试使用 QString
作为 std::unordered_map
中的键,但是我收到错误消息:
error C2280: 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': attempting to reference a deleted function
我无法切换到 QHash
,因为地图的值类型是不可复制的。有什么办法可以做到这一点吗?
问题是没有 std::hash<QString>()
专业化。根据 dbj2 算法定义您自己的具有相当好的性能的算法很容易:
#include <QString>
#include <unordered_map>
namespace std
{
template<> struct hash<QString>
{
std::size_t operator()(const QString& s) const noexcept
{
const QChar* str = s.data();
std::size_t hash = 5381;
for (int i = 0; i < s.size(); ++i)
hash = ((hash << 5) + hash) + ((str->row() << 8) | (str++)->cell());
return hash;
}
};
}
将其包含在 std::unordered_map
中使用 QString
的文件中,错误消失。
将 hash
实现放在 header 中,并确保在使用地图的任何地方都包含 header。
转发到 qHash
的简单实现应该就足够了:
#include <QHash>
#include <QString>
#include <functional>
namespace std {
template<> struct hash<QString> {
std::size_t operator()(const QString& s) const noexcept {
return (size_t) qHash(s);
}
};
}
尽管在常见的 64 位平台上 std::size_t
大于 unsigned int
,因此哈希不会在其整个长度上发生变化 - 这不是问题。该标准对 std::hash
实施没有这样的要求。
但我们不要忘记,修改 std
命名空间中的任何内容通常是未定义的行为,是不必要的。
长话短说:
您可以特化某些 type 和 variable 模板,前提是特化至少依赖于一种 user-defined 类型。您不能完全专注于 built-in 或 C++ 库类型。
参考Extending the namespace std。在那里,我们读到:
It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below.
主要是允许在 std
命名空间中特化某些 类型 :
It is allowed to add template specializations for any standard library class template to the namespace std only if the declaration depends on at least one program-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited.
请注意,合法的是类型,即类。函数和成员函数?从来没有:
It is undefined behavior to declare a full specialization of any standard library function template, [... or] member function of a standard library class template, [... or] member function template of a standard library class or class template.
另一个有限的例外是变量模板:
It is undefined behavior to declare a full or partial specialization of any standard library variable template, except where explicitly allowed.
在所有情况下都强调我的。一如既往,还有 details that one should get acquainted with.
似乎在较新版本的 Qt 中,std::hash 是为 QString 定义的,因此您可以直接将其与 std::unordered_map 一起使用。 (它适用于我机器上的 Qt 5.14。)