C++ 理解运算符查找;哪个编译器是正确的?
这可能会被问到,但我没有运气找到答案 我有一个无序容器(即散列;我们称之为QHash,因为它需要一个比较运算符来表示它的键类型,尽管这可能发生在任何类似的情况下) 考虑以下几点: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
// 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; }