c++qtqhash

How to use std::string as key of QHash?


I want to use a std::string as the key of a QHash:

QHash<std::string, QString> m_hash;
m_hash.insert("ABC", "DEF");

I implemented the required qHash:

inline qHash(const std::string& key, uint seed = 0)
{
  qHash(QByteArray::fromRawData(key.data(), key.length()), seed);
}

Everything compiles correctly using MSVC, but gcc generates the following error:

error: no matching function for call to qHash(const std::__cxx11::basic_string<char>&)

How should I resolve this isse?


Solution

  • Qt 6.1 and newer

    Using std::string as a key is supported out of the box, as Qt now uses std::hash as a fallback for qHash.

    Pre Qt 6.1

    Short answer

    Defining the qHash function inside the std namespace may resolve the compiler error, but adding a function to the std namespace is undefined behaviour. So, it is not allowed.

    A workaround is to wrap std::string inside a helper class[1]:

    class MyHashString : public std::string {};
    QHash<MyHashString, QString> m_hash;
    m_hash.insert("ABC", "DEF");
    
    inline qHash(const MyHashString& key, uint seed = 0)
    {
      qHash(QByteArray::fromRawData(key.data(), key.length()), seed);
    }
    

    Long Answer

    As stated in this bug report, one has to define the qHash function inside the namespace of std::string:

    This is documented in the C++ standard. It's called Argument-Dependent Lookup. It says that searching for an unqualified qHash(T) will find it in T's namespace.

    So, the correct definition of the required qHash would be:

    namespace std
    {
      inline qHash(const std::string& key, uint seed = 0)
      {
        qHash(QByteArray::fromRawData(key.data(), key.length()), seed);
      }
    }
    

    It is also mentioned in the Qt docs:

    A QHash's key type has additional requirements other than being an assignable data type: it must provide operator==(), and there must also be a qHash() function in the type's namespace that returns a hash value for an argument of the key's type.

    However, adding a function to the std namespace is undefined behaviour. So, one is stuck using a non-ideal workaround.

    Further reading