C++ 理解运算符查找;哪个编译器是正确的?

C++ 理解运算符查找;哪个编译器是正确的?,c++,templates,namespaces,operator-overloading,C++,Templates,Namespaces,Operator Overloading,这可能会被问到,但我没有运气找到答案 我有一个无序容器(即散列;我们称之为QHash,因为它需要一个比较运算符来表示它的键类型,尽管这可能发生在任何类似的情况下) 考虑以下几点: // foo.h class Bar { public: class Foo {}; }; // foo.cpp #include <QtCore/QHash> namespace { typedef Bar::Foo Foo; bool operator==(Foo const

这可能会被问到,但我没有运气找到答案

我有一个无序容器(即散列;我们称之为QHash,因为它需要一个比较运算符来表示它的键类型,尽管这可能发生在任何类似的情况下)

考虑以下几点:

// foo.h
class Bar
{
public:
    class Foo {};
};

// foo.cpp
#include <QtCore/QHash>

namespace
{
    typedef Bar::Foo Foo;
    bool operator==(Foo const& a, Foo const& b) { return &a == &b; }
}

uint qHash(Foo const& foo) { return qHash(&foo); }

int main()
{
    QHash<Foo, int> hash;
    // do stuff with hash, e.g.:
    hash.insert(Foo(), 5);
    return 0;
}
//foo.h
分类栏
{
公众:
类Foo{};
};
//foo.cpp
#包括
名称空间
{
typedef条::Foo-Foo;
布尔运算符==(Foo const&a,Foo const&b){return&a==&b;}
}
uint-qHash(Foo-const&Foo){return-qHash(&Foo);}
int main()
{
QHash散列;
//使用散列进行填充,例如:
insert(Foo(),5);
返回0;
}
使用G++,一切都很好。但是,clang在qhash.h的内部给出了一个关于二进制表达式的
无效操作数的错误,其中尝试在
Foo
的实例上使用
=
。在我看来,clang要么没有在匿名名称空间中找到要么拒绝了
operator==
的定义,可能是因为与G++不同的查找规则

我想知道哪个编译器是正确的


p、 尽管您声称无序容器的选择无关紧要,但以下自包含的完全标准代码在g++和clang上都会生成一致的错误——两者都找不到
运算符==

#include <unordered_map>

// foo.h
class Bar
{
public:
    class Foo {};
};

// foo.cpp
namespace
{
    typedef Bar::Foo Foo;
    bool operator==(Foo const& a, Foo const& b) { return &a == &b; }
}

namespace std
{
  template <>
  struct hash<Foo>
  {
    std::size_t operator()(const Foo& k) const
    {
      return std::hash<const Foo*>()(&k);
    }
  };
}

int main()
{
    std::unordered_map<Foo, int> hash;
    // do stuff with hash, e.g.:
    hash.emplace(Foo(), 5);
    return 0;
}

基本上,如果您希望通过库代码找到运算符,请将它们粘贴到类的关联命名空间中,并让ADL完成它的工作。绝对不要使用匿名名称空间,除非类本身位于匿名名称空间内。

好的,ADL会查看定义类的名称空间。看 在 [basic.lookup.argdep]/2: 对于函数调用中的每个参数类型T,都有一组零 一个或多个关联的命名空间和 要考虑的零个或多个关联类的集合 名称空间和类是确定的 完全由函数参数的类型(以及 任何模板参数)。 Typedef名称和用于指定类型的使用声明不适用于 为这一组贡献力量。”

注意最后一句话

因此,对于原始示例,在 接线员不帮忙, gcc是错误的,clang是正确的。

这是一个错误。从本质上讲,问题在于GCC没有正确地实现运算符名称的两阶段名称查找。您的
运算符==
不是通过非限定查找找到的(因为声明太晚),也不是通过依赖参数的名称查找找到的(因为
::(匿名名称空间)
不是
Bar::Foo
的关联名称空间,因此不应在
qHash
的实例化中考虑


如果将
qHash
的定义移动到匿名名称空间中,GCC将拒绝该代码,因为它的错误只适用于运算符名,而不适用于普通函数名。

内容很重要。我们需要一个实际的可编译示例和错误消息。让so读者猜测产生的是什么密码是好主意我看到至少有4个人已经这样做了,所以我投了弃权票。@jrok,不,他们真的没有(事实上,空就可以了)。问题是:“鉴于上述情况,QHash尝试在一对Foo上使用operator==会找到定义的运算符吗?”.GCC认为“是”。Clang认为“否”。我不是在问如何修复它(“不要这样做”/使操作符==公开);我想知道哪个是正确的……在有人告诉我需要解释无序容器的概念之前……QHash是(显然)
上的模板类,其关联的助手类
QHashNode
具有成员
键和方法
(键常量和键0)
这是一个
key0==key
。这里会发出叮当声,抱怨它找不到合适的
==/code>。那么,你对
std::unordered\u map
也有同样的问题吗?我说key类的内容不重要。它们不重要。我认为容器类“可能”不重要,但显然它确实重要。(抱歉;因此将输入注释视为“是的,我想发布”…没有完成…)然而,虽然我认为您暗示了答案,但我想问的是,GCC是否也应该拒绝原始代码(就像clang那样)?@Matthew:如果没有容器的完整源代码,就无法知道。如果您希望所有容器都能找到
操作符==
,请将其放在相关的命名空间中。它不是“我的”容器…我想你从来没有听说过Qt?请看@Matthew:我听说过,但你已经决定使用它。对你的决定负责。当你开始依赖内部实现细节时(你是这样的,因为如果替换了标准
std::unordered_map
,你写的代码就不起作用了)然后,您需要将这些实现细节视为您的代码。否则,您将面临未来Qt更改破坏整个应用程序的风险。谢谢;这是非常正确的!除了使用运算符的命名空间也会影响查找,OP没有告诉我们调用代码所在的命名空间。
  { return __x == __y; }