【问题标题】:Using QString as the key in std::unordered_map使用 QString 作为 std::unordered_map 中的键
【发布时间】:2018-07-09 14:46:34
【问题描述】:

我正在尝试使用QString 作为std::unordered_map 中的键,但是我得到了错误:

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

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

【问题讨论】:

    标签: c++ qt c++17 stdhash


    【解决方案1】:

    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 在常见的 64 位平台上大于 unsigned int,因此散列不会在其全长上发生变化 - 这不是问题。该标准对 std::hash 实现没有这样的要求。


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

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

    请参阅Extending the namespace std 以供参考。在那里,我们读到:

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

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

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

    请注意,合法的是专门化类型,即类。函数和成员函数?从来没有:

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

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

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

    在所有情况下都强调我的。与往常一样,还有更多details that one should get acquainted with

    【讨论】:

      【解决方案2】:

      问题是没有std::hash&lt;QString&gt;() 专业化。根据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 的文件中包含它,错误就会消失。

      【讨论】:

      • 您可以将其简化为return std::hash&lt;std::string&gt;()(s.toStdString());
      • @RSahu 当然可以,但由于 unicode 到 utf8 的转换和内存分配,它的性能不会那么好。
      • 甚至可能是return qHash(s)
      • @NicolasHolthaus,这是真的。
      • 如前所述,仅返回 qHash 值更简单。
      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2012-07-09
        • 2020-03-09
        • 1970-01-01
        • 2013-05-04
        • 1970-01-01
        • 2017-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多