使用标准库类型作为QHASH或QSET中的键

Using standard library types as keys in QHash or QSet

本文关键字:QSET QHASH 标准 类型      更新时间:2023-10-16

qt需要在QT的QHash模板中使用密钥类型使用的qHash超载。根据文档,重载必须是"类型的名称空间"。但这是一个问题,因为在C 中,将新的过载添加到std名称空间中是未定义的行为。仅将过载添加到全局名称空间也不起作用。

最小示例:

#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名称空间中。

更具体地说,必须将其注入到ADL-Oug-lookaup尝试的命名空间中。这可能涉及追踪错误的命名空间。


如果您正在设计图书馆并想避免此问题,则应创建一个专门解决此问题的名称空间。

创建两个功能:internal_qHashqHash

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_supportT的任何关联名称空间。

如果我们想检测Sfinae,必须完成更多的工作。

在此模型中,您要为无法将功能注入namespace mylibrary::hash_support的命名空间的过载。

QT可能已经在为qHash(int, uint)做类似的事情。查看QT定义的位置,如果您定义了std超载,则应起作用。

qhash_tag强制将2相名称查找重新考虑在点qHash处注入mylibrary::hash_support中的新符号,用于给定类型。