之前可能会问过,但我没有找到答案的运气......
我有一个无序的容器(即一个哈希;我们称之为QHash,因为它可能会发生在任何类似的情况下),它需要一个比较运算符才能获得它的密钥类型。 / p>
请考虑以下事项:
// 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;
}
使用G ++,一切都很好。但是,clang在qhash.h的内容中给出了一个错误invalid operands to binary expression
,其中==
试图在Foo
的实例上使用operator==
。在我看来,clang要么没有找到或拒绝匿名命名空间中{{1}}的定义,可能是因为查找规则与G ++不同。
我想知道,哪个编译器是正确的?
P.S。我在C ++ 11模式下构建,以防它有所作为。
答案 0 :(得分:5)
好吧,ADL会查看定义类的命名空间。看看 在 [basic.lookup.argdep] / 2: &#34;对于函数调用中的每个参数类型T,都有一组零 或更多关联的命名空间和a 要考虑的零个或多个关联类的集合。套 命名空间和类已确定 完全由函数参数的类型(和。的名称空间) 任何模板模板参数)。 Typedef名称和用于指定类型的using声明不会 为这一集做出贡献。&#34;
请注意最后一句。
因此,对于原始示例,命名空间中的typedef 运营商没有帮助, gcc错了,clang是对的。
答案 1 :(得分:5)
这是known GCC bug。本质上,问题是GCC没有正确实现运营商名称的两阶段名称查找。非限定查找找不到operator==
(因为声明太晚了),并且找不到依赖于参数的名称查找(因为::(anonymous namespace)
不是Bar::Foo
的关联命名空间,所以不应在qHash
的实例化中考虑。
如果将qHash
的定义移到匿名命名空间中,GCC将拒绝该代码,因为它的错误仅适用于操作员名称而不适用于正常的函数名称。
答案 2 :(得分:-1)
尽管您声称选择无序容器并不重要,但以下自包含的完全标准代码会在g ++和clang上生成一致的错误 - 两者都找不到operator==
。
#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;
}
错误的重要部分是
错误:
operator==
不匹配(操作数类型为const Bar::Foo
和const Bar::Foo
){ return __x == __y; }
基本上,如果您希望通过库代码找到运算符,请将它们放在类的关联命名空间中,让ADL执行它的操作。绝对不要使用匿名命名空间,除非该类本身位于匿名命名空间内。