In the previous blog post of this series, we discussed KDToolBox::QtHasher, a helper class that allows us to use unordered associative containers datatypes which have a qHash()
overload but not a std::hash
specialization. The majority of Qt value classes indeed are lacking such a specialization, even if they do provide a qHash()
overload.
For our datatypes, having to provide both qHash
and std::hash
(if you want to store them in either QHash/QSet or std::unordered_map/set without the need of a customer hasher) is...mildly annoying. In a project, I've even resorted to using a macro to implement one function in terms of the other one.
This consideration led me to ask myself, "why can't QHash itself support std::hash
directly?" This would allow me to implement just one hashing function, and not two. Cherry on top: it would allow us to use QHash/QSet datatypes that only offer std::hash
and not qHash
, such as the ones coming from the Standard Library! (You may want to read here about why it's actually impossible to reliably add a qHash
overload for them.)
A quick patch later, and now, finally, Qt also supports the usage of std::hash
as a hashing function. For a given datatype, Qt will now implement a "fallback" mechanism -- it's going to call the first callable option between:
qHash(T, seed)
qHash(T)
std::hash()(t, seed)
std::hash()(t)
Using the third option with a defaulted seed argument allows Qt to still provide a seed to your hashing function, and your type to still be usable in a Standard Library associative container:
The support for std::hash
in Qt's containers comes with a minor limitation: you can't use a custom hashing object, like you can in containers from the Standard Library. Qt is going to default construct your std::hash
specialization. Honestly, I've practically never seen a specialization that isn't trivially default constructible anyhow.
The end of qHash
?
If the Qt containers support std::hash
, what does it mean for qHash
? Well, the support for qHash
inside Qt's associative containers is likely to never go away -- this is part of the strong source compatibility promise of Qt. (If I had to imagine, qHash
may be deprecated in favour of std::hash
in Qt 7, and removed in Qt 8. But this is 100% speculation, at this point in time...). But even offering qHash
overloads for Qt datatypes is part of the public API of Qt, which means we can't just remove those overloads. For instance, they are used all over the place in the implementation of qHash
for end-user datatypes:
Can Qt easily offer std::hash
specializations for its own types?
Not so easily, I'm afraid -- it would require a massive effort to complement each and every qHash
overload with a corresponding std::hash
implementation (or outright replace those overloads, if you think qHash
should go away). I just don't see this as being done anytime soon, and that's why a solution like KDToolBox::QtHasher still makes sense, for the time being.
A similar effort would also be needed if we wanted to offer a constrained version of std::hash
. We would need a way to "tag" each Qt datatype (by using some type trait). Then, Qt could offer a centralized:
Did you notice that I'm using constraints and concepts (C++20) here? Such a solution is also unfeasible in C++17, because std::hash
does not offer us a "SFINAE hook." But that's a discussion for another time...
Thanks for reading!
Trusted software excellence across embedded and desktop platforms
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.