16

我正在尝试QString用作 a 中的密钥std::unordered_map,但是出现错误:

错误 C2280:'std::hash<_Kty>::hash(const std::hash<_Kty> &)':试图引用已删除的函数

我无法切换到, QHash因为地图的值类型是不可复制的。有什么办法可以使这项工作?

4

3 回答 3

24

hash实现放在标题中,并确保在使用地图的任何地方都包含该标题。

转发到的简单实现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);
    }
  };
}

尽管std::size_t它比unsigned int常见的 64 位平台大,因此散列不会在其全长上发生变化 - 这不是问题。该标准对std::hash实现没有这样的要求。


但是,我们不要忘记,修改std命名空间中的任何内容通常都是未定义的行为并且是不必要的。

TL;DR:
您可以专门化某些类型变量模板,并且仅当专门化依赖于至少一种用户定义的类型时。您不能完全专注于内置或 C++ 库类型。

请参阅扩展命名空间 std以供参考。在那里,我们读到:

将声明或定义添加到命名空间 std 或任何嵌套在 std 中的命名空间是未定义的行为,下面会提到一些例外情况。

主要是允许在命名空间中特化某些类型std

仅当声明依赖于至少一种程序定义类型并且特化满足原始模板的所有要求时,才允许将任何标准库类模板的模板特化添加到命名空间 std,除非此类特化被禁止。

请注意,合法的是专门化类型,即类。函数和成员函数?永远不会:

声明任何标准库函数模板的完全特化、标准库类模板的 [... 或] 成员函数、标准库类或类模板的 [... 或] 成员函数模板是未定义的行为。

另一个有限的例外是变量模板:

声明任何标准库变量模板的全部或部分特化是未定义的行为,除非明确允许

在所有情况下都强调我的。与往常一样,还有更多细节需要了解

于 2018-01-30T18:15:59.527 回答
8

问题是没有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;
        }
    };
}

将其包含在使用 a 的文件中QStringstd::unordered_map错误就会消失。

于 2018-01-30T16:24:39.167 回答
1

似乎在 Qt 的较新版本中,为 QString 定义了 std::hash ,因此您可以直接将其与 std::unordered_map 一起使用。(它适用于我机器上的 Qt 5.14。)

于 2021-04-08T09:31:22.670 回答