C++ 使用标准库类型作为QHash或QSet中的键

C++ 使用标准库类型作为QHash或QSet中的键,c++,qt,C++,Qt,Qt需要Qt的qHash模板中使用的密钥类型的qHash重载。根据这一点,重载需要“在类型的命名空间中”。但这是一个问题,因为在C++中,新的重载添加到 STD< /COD>命名空间。仅仅将重载添加到全局名称空间也不起作用 一个简单的例子: #include <QHash> #include <string> //namespace std { // when adding to namespace std it compilies but that is not al

Qt需要Qt的
qHash
模板中使用的密钥类型的
qHash
重载。根据这一点,重载需要“在类型的命名空间中”。但这是一个问题,因为在C++中,新的重载添加到<代码> STD< /COD>命名空间。仅仅将重载添加到全局名称空间也不起作用

一个简单的例子:

#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);
}
#包括
#包括
//名称空间std{//当添加到名称空间std时,它会编译,但这是不允许的
静态uint qHash(const std::u32string&key,uint seed)无异常{
返回静态_cast(std::hash{}(key));
}
//}
qh;
int main(int argc,字符**argv){
h、 插入(std::u32string(),5);
}
生成的错误消息相当长,我省略了已尝试的候选列表(它们不包含字符串重载)

在/usr/include/x86_64-linux-gnu/qt5/QtCore/qglobal.h:83:0中包含的文件中,
来自/usr/include/x86_64-linux-gnu/qt5/QtCore/qcoreapplication.h:43,
来自/usr/include/x86_64-linux-gnu/qt5/QtCore/QCoreApplication:1,
来自../test.cpp:1:
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:在“uint-qHash(const-T&,uint)[with T=std::_-cxx11::basic_-string;uint=unsigned int]的实例化中:
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhash.h:920:18:必须来自'qhash::Node**qhash::findNode(const Key&,uint*)const[with Key=std::uu cxx11::basic_string;T=int;qhash::Node=QHashNode;uint=unsigned int]'
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhash.h:760:27:从'qhash::iterator qhash::insert(const Key&,const T&)[with Key=std::u cxx11::basic_string;T=int]中必须输入'
../qttui/testtui2.cpp:15:33:此处为必填项
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:110:40:错误:调用“qHash(const std::u cxx11::basic_string&”)时没有匹配的函数
Q_DECL_NOEXCEPT_EXPR(NOEXCEPT(qHash(t)))
~~~~~^~~
/usr/include/x86_64-linux-gnu/qt5/QtCore/qcompilerdetection.h:1144:43:注意:在宏“Q_DECL_NOEXCEPT_EXPR”的定义中
#定义Q_DECL_NOEXCEPT_EXPR(x)NOEXCEPT(x)
^
在/usr/include/x86_64-linux-gnu/qt5/QtCore/qlist.h:47:0中包含的文件中,
来自/usr/include/x86_64-linux-gnu/qt5/QtCore/qobject.h:49,
来自/usr/include/x86_64-linux-gnu/qt5/QtCore/qcoreapplication.h:46,
来自/usr/include/x86_64-linux-gnu/qt5/QtCore/QCoreApplication:1,
来自测试。cpp:4,
/usr/include/x86_64-linux-gnu/qt5/QtCore/qhashfunctions.h:72:52:注:候选:constexpr uint qHash(char,uint)
Q_DECL_CONST_函数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:注意:参数1从“const std::u cxx11::basic_string”到“char”的转换未知
错误消息中的代码如下(据我在全局命名空间中所知):

模板内联uint qHash(常量T&T,uint种子)
Q_DECL_NOEXCEPT_EXPR(NOEXCEPT(qHash(t))///numBuckets | ahp){

h=qHash(akey,d->seed);//将重载放入正在进行查找的Qt命名空间中

更具体地说,它必须被注入到发生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_标记{};
uint qHash(qHash_标记,int const&t,uint seed){/*TODO*/}
}
使用::mylibrary::hash_support::qhash_标记;
模板
constexpr uint internal_qHash(T const&T,uint seed){
使用::mylibrary::hash_support::qHash;
返回qHash(qHash_标记{},t,seed);
}
命名空间哈希\u adl\u阻塞{
模板
constexpr uint qHash(T const&T,uint seed){
return::mylibrary::internal_qHash(t,seed);
}
}
使用::mylibrary::hash_adl_blocking::qHash;
}
现在使用
mylibrary::qHash
进行基于ADL的查找,并支持
mylibrary::hash\u
T
的任何相关名称空间

如果我们想检测SFINAE,还需要做更多的工作

在这个模型中,您将为不能在
名称空间mylibrary::hash_support
中插入函数的名称空间创建重载

Qt可能已经在为
qHash(int,uint)
做类似的事情。看看Qt在哪里定义了它,如果你在那里定义了
std
重载,它应该可以工作


qhash\u标记
强制两阶段名称查找重新考虑注入
mylibrary::hash\u支持
的新符号,此时
qhash
是为给定类型实例化的。

@AlexanderVX:如果我用
返回0替换重载主体,我会得到相同的错误;
这是一个qt而不是一个boost问题;).qt doe默认情况下,它的api不使用名称空间。据我所知,使用尝试发生在全局名称空间中。不知何故,g++似乎在qhashfunctions.h:110处看到了模板,比看起来的重载更具体。(我也将该代码添加到问题中)@textshell oops。但是用Qt替换boost会保留完整的答案。查找定义了
qHash(int,uint)
的位置。问题是它们没有在名称空间中定义(即在全局级别)。宏
QT\u BEGIN\u NAMESPACE
在正常的QT安装中为空。现有定义和查找都发生在全局命名空间中。因此,我甚至不知道
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);
}
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;
}