Qt 需要 Qt 的 QHash
模板中使用的键类型的 qHash
重载。根据the documentation此重载需要“在类型的命名空间中”。但这是一个问题,因为在 c++ 中向 std
命名空间添加新的重载 is undefined behaviour .仅将重载添加到全局 namespace 也不起作用。
一个最小的例子:
#include <QHash>
#include <string>
//namespace std { // when adding to namespace std it compilies but that is not allowed
static uint qHash(const std::u32string &key, uint seed) noexcept {
return static_cast<uint>(std::hash<std::u32string>{}(key));
}
//}
QHash<std::u32string, int> h;
int main(int argc, char **argv) {
h.insert(std::u32string(), 5);
}
生成的错误消息相当长,我省略了已尝试的候选列表(它们不包含字符串重载)
In file included from /usr/include/x86_64-linux-gnu/qt5/QtCore/qglobal.h:83:0,
from /usr/include/x86_64-linux-gnu/qt5/QtCore/qcoreapplication.h:43,
from /usr/include/x86_64-linux-gnu/qt5/QtCore/QCoreApplication:1,
from ../test.cpp:1:
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h: In instantiation of ‘uint qHash(const T&, uint) [with T = std::__cxx11::basic_string<char32_t>; uint = unsigned int]’:
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhash.h:920:18: required from ‘QHash<K, V>::Node** QHash<K, V>::findNode(const Key&, uint*) const [with Key = std::__cxx11::basic_string<char32_t>; T = int; QHash<K, V>::Node = QHashNode<std::__cxx11::basic_string<char32_t>, int>; uint = unsigned int]’
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhash.h:760:27: required from ‘QHash<K, V>::iterator QHash<K, V>::insert(const Key&, const T&) [with Key = std::__cxx11::basic_string<char32_t>; T = int]’
../qttui/testtui2.cpp:15:33: required from here
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:110:40: error: no matching function for call to ‘qHash(const std::__cxx11::basic_string<char32_t>&)’
Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(t)))
~~~~~^~~
/usr/include/x86_64-linux-gnu/qt5/QtCore/qcompilerdetection.h:1144:43: note: in definition of macro ‘Q_DECL_NOEXCEPT_EXPR’
# define Q_DECL_NOEXCEPT_EXPR(x) noexcept(x)
^
In file included from /usr/include/x86_64-linux-gnu/qt5/QtCore/qlist.h:47:0,
from /usr/include/x86_64-linux-gnu/qt5/QtCore/qobject.h:49,
from /usr/include/x86_64-linux-gnu/qt5/QtCore/qcoreapplication.h:46,
from /usr/include/x86_64-linux-gnu/qt5/QtCore/QCoreApplication:1,
from test.cpp:4,
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:72:52: note: candidate: constexpr uint qHash(char, uint)
Q_DECL_CONST_FUNCTION Q_DECL_CONSTEXPR inline uint qHash(char key, uint seed = 0) Q_DECL_NOTHROW { return uint(key) ^ seed; }
^~~~~
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:72:52: note: no known conversion for argument 1 from ‘const std::__cxx11::basic_string<char32_t>’ to ‘char’
错误消息中的代码是这样的(据我在全局命名空间中所知):
template<typename T> inline uint qHash(const T &t, uint seed)
Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(t))) // <---- qhashfunctions.h:110
{ return qHash(t) ^ seed; }
和:
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::Node **QHash<Key, T>::findNode(const Key &akey, uint *ahp) const
{
uint h = 0;
if (d->numBuckets || ahp) {
h = qHash(akey, d->seed); // <---- qhash.h:920
if (ahp)
*ahp = h;
}
return findNode(akey, h);
}
最佳答案
将重载放入进行查找的 Qt 命名空间。
更具体地说,它必须被注入(inject)到 ADL 查找尝试发生的命名空间中。这可能涉及追踪错误发生在哪个命名空间。
如果你正在设计一个库,想避免这个问题,你应该专门创建一个命名空间来解决这个问题。
创建两个函数:internal_qHash
和 qHash
。
namespace mylibrary {
namespace hash_support {
struct qhash_tag {};
uint qHash( qhash_tag, int const& t, uint seed ) { /* TODO */ }
}
using ::mylibrary::hash_support::qhash_tag;
template<class T>
constexpr uint internal_qHash( T const& t, uint seed) {
using ::mylibrary::hash_support::qHash;
return qHash( qhash_tag{}, t, seed );
}
namespace hash_adl_blocking {
template<class T>
constexpr uint qHash( T const& t, uint seed ) {
return ::mylibrary::internal_qHash( t, seed );
}
}
using ::mylibrary::hash_adl_blocking::qHash;
}
现在使用 mylibrary::qHash
对 mylibrary::hash_support
和 T
的任何关联命名空间进行基于 ADL 的查找。
如果我们想要检测 SFINAE,还需要做更多的工作。
在此模型中,您将为无法将函数注入(inject)到 namespace mylibrary::hash_support
中的命名空间创建重载。
Qt 可能已经在为 qHash(int, uint)
做类似的事情。看看 Qt 在哪里定义它,如果你在那里定义你的 std
重载,它应该可以工作。
qhash_tag
强制 2 阶段名称查找重新考虑在 qHash
实例化时注入(inject)到 mylibrary::hash_support
中的新符号给定的类型。
关于c++ - 使用标准库类型作为 QHash 或 QSet 中的键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47460098/