我试图将 QString
作为 std::unordered_map
中的键,但是我收到了以下错误:
error C2280: 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': 尝试引用已删除的函数
我无法切换到 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
更大,因此哈希值在其整个长度范围内不会改变,但这并不是问题。标准对std::hash
实现没有这样的要求。
不过要记住,修改std命名空间中的任何内容通常是未定义行为且不必要的。
简而言之:
您可以特化某些类型和变量模板,但仅当特化取决于至少一个用户定义的类型时才可以。您无法完全为内置类型或C++标准库类型进行特化。
有关详细信息,请参见扩展命名空间std。在那里,我们可以看到:
向std命名空间或嵌套在std中的任何命名空间添加声明或定义都是未定义行为,但有一些例外情况,如下所示。
主要的例外是允许特化std命名空间中的某些类型:
只有在声明依赖于至少一个程序定义的类型,并且特化满足原始模板的所有要求(除非禁止这种特定),才允许为任何标准库类模板添加模板特化到命名空间std中。
请注意,合法的是对类型进行特化,即类。函数和成员函数呢?从不:
声明任何标准库函数模板的完全特化[...或]标准库类模板的成员函数[...或]标准库类或类模板的成员函数模板是未定义行为。
另一个有限的例外是变量模板:
声明任何标准库变量模板的完全或部分特化,除非明确允许,都是未定义行为。
无论如何,始终有需要了解的进一步细节。
问题在于没有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
的文件中,就可以消除错误。
在较新版本的Qt中,似乎已经为QString定义了std::hash,因此您可以直接将其与std::unordered_map一起使用。(在我的机器上,它适用于Qt 5.14。)
return std::hash<std::string>()(s.toStdString());
。 - R SahuqHash(s)
。 - SteakOverflow