C++ STL中运算符==的命名空间解析

C++ STL中运算符==的命名空间解析,c++,stl,namespaces,operator-overloading,argument-dependent-lookup,C++,Stl,Namespaces,Operator Overloading,Argument Dependent Lookup,考虑一个简单类型,在名称空间中,使用运算符==: 命名空间ANamespace{ 结构Foo{inti;float f;}; } #ifdef内部 命名空间ANamespace{ 布尔运算符==(常量Foo&l、常量Foo&r) { 返回l.i==r.i&&l.f==r.f; } } #否则 布尔运算符==(常量ANamespace::Foo&l,常量ANamespace::Foo&r) { 返回l.i==r.i&&l.f==r.f; } #恩迪夫 布尔比较元素(常量标准::向量和l,常量标准:

考虑一个简单类型,在名称空间中,使用
运算符==

命名空间ANamespace{
结构Foo{inti;float f;};
}
#ifdef内部
命名空间ANamespace{
布尔运算符==(常量Foo&l、常量Foo&r)
{
返回l.i==r.i&&l.f==r.f;
}
}
#否则
布尔运算符==(常量ANamespace::Foo&l,常量ANamespace::Foo&r)
{
返回l.i==r.i&&l.f==r.f;
}
#恩迪夫
布尔比较元素(常量标准::向量和l,常量标准::向量和r)
{
返回l==r;
}
如果在
ANamespace
中定义了
operator==
(通过在
中定义
),则编译该示例。但是,如果在全局名称空间中定义了
操作符==
#else
案例),则函数
compareElements()
不会编译-在GCC和Clang中,以及在libstdc++和libc++中。所有都会沿以下行发出模板错误:

In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/vector:60:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/stl_algobase.h:820:22: error: invalid operands to binary expression ('const ANamespace::Foo' and 'const ANamespace::Foo')
            if (!(*__first1 == *__first2))
                  ~~~~~~~~~ ^  ~~~~~~~~~
...
但是,直接比较函数中的两个
Foo
s,例如

bool compareDirectly(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
    return l == r;
}
无论在何处定义了
操作符==
,似乎都可以正常工作

标准中是否有关于STL需要定义
运算符==
的规则?

!(*.\u first1==*.\u first2)
发生在函数模板
std::operator==
中,因此它被视为一个依赖的非限定函数调用表达式,因此在重载解析过程中,只有在
std::operator=
的定义上下文中找到的函数和通过ADL找到的函数是候选函数


显然,在标准比较运算符定义的上下文中没有声明
运算符==(const Foo&,const Foo&)
。在参数相关查找(ADL)中,检查每个参数的名称空间以搜索调用的可行函数,这就是为什么在
ANamespace
中定义
运算符==
有效的原因。

简言之,在声明类的同一命名空间中声明
operator==
,可以保证依赖于参数的查找将找到它,因此您应该这样做。本标准并不要求您遵守本公约,但实际上这是获得担保的唯一途径。这也适用于标准库可能对您的类型调用的其他运算符

如果您选择在全局命名空间中声明
运算符==
,但您的类型未在全局命名空间中声明,则标准库算法仍有可能通过非限定名称查找找到您的
运算符==
。但是,这并不能保证有效,因为非限定名称查找将在找到
operator==
的最内层封闭名称空间停止。换句话说,在一个算法的形式

namespace std {
template< class InputIt1, class InputIt2 >
constexpr bool equal( InputIt1 first1, InputIt1 last1,
                      InputIt2 first2 ) {
    // ...
}
}
名称空间std{
模板
constexpr bool equal(InputIt1 first1,InputIt1 last1,
输入2(2){
// ...
}
}
operator==
的非限定名称查找将找到
std
命名空间中声明的任何
operator==
s(当然,这不适用于您的用户定义类型),然后,如果它在
std
中找到任何内容,即使它可能不是可行的重载,将不会在全局命名空间中查找。

您需要详细阅读“ADL”又名“参数相关查找”

基本上,当您编写
v1==v2
时,编译器会在当前命名空间中查找一个
运算符==
,该运算符使用两个正确类型的参数
ANamespace::Foo
。(注意:我们在这里忽略转换)。如果找不到,那么它将查找定义类型的命名空间(
ANamespace


有一篇关于这方面的文章。

我假设这个问题与ADL有关,但这个答案并没有真正解释ADL是如何/为什么在这里发挥作用的。