使用 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 命名空间中的任何内容通常是未定义的行为,是不必要的。

长话短说:
您可以特化某些 typevariable 模板,前提是特化至少依赖于一种 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。)