STL 中使用 operator== 的命名空间解析
Namespace resolution with operator== in the STL
考虑一个简单的类型,在一个命名空间中,带有 operator==
:
namespace ANamespace {
struct Foo { int i; float f; };
}
#ifdef INSIDE
namespace ANamespace {
bool operator==(const Foo& l, const Foo& r)
{
return l.i == r.i && l.f == r.f;
}
}
#else
bool operator==(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l.i == r.i && l.f == r.f;
}
#endif
bool compareElements(const std::vector<ANamespace::Foo>& l, const std::vector<ANamespace::Foo>& r)
{
return l == r;
}
如果在 ANamespace
中定义了 operator==
(通过定义 INSIDE
),该示例将编译。但是,如果 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
,例如
bool compareDirectly(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l == r;
}
似乎无论在哪里定义 operator==
都可以正常工作。
标准中是否有关于 STL 期望 operator==
定义的位置的规则?
!(*__first1 == *__first2)
发生在函数模板 std::operator==
中,因此它被认为是依赖的非限定函数调用表达式,因此在重载解析期间仅在 [=11= 的定义上下文中找到函数] 和那些通过 ADL 找到的是候选人。
很明显,在标准比较运算符的定义上下文中没有声明 operator==(const Foo&, const Foo&)
。在参数相关查找 (ADL) 中,检查每个参数的名称空间以搜索调用的可行函数,因此这就是为什么在 ANamespace
中定义 operator==
有效。
简而言之,在声明 class 的同一个命名空间中声明 operator==
可以保证依赖于参数的查找会找到它,所以这就是您应该做的。该标准并未强制要求您遵循此约定,但在实践中这是获得保证的唯一途径。这也适用于标准库可能对您的类型调用的其他运算符。
如果您选择在全局命名空间中声明 operator==
但您的类型是 而不是 在全局命名空间中声明,标准库算法有可能会仍然可以通过不合格的名称查找找到您的 operator==
。但是,不能保证这有效,因为不合格的名称查找将 停止 在找到 operator==
的最内层封闭命名空间。换句话说,在
形式的算法中
namespace std {
template< class InputIt1, class InputIt2 >
constexpr bool equal( InputIt1 first1, InputIt1 last1,
InputIt2 first2 ) {
// ...
}
}
operator==
的非限定名称查找将找到在 std
命名空间中声明的任何 operator==
(当然,这不适用于您的用户定义类型)和然后,如果它在 std
中找到任何内容,即使它可能不是可行的重载,也不会在全局命名空间中查找。
您需要阅读 "ADL" 又名 "Argument Dependent Lookup"。
基本上,当您编写 v1 == v2
时,编译器会在当前命名空间中寻找带有两个正确类型 ANamespace::Foo
参数的 operator==
。 (注意:我们在这里忽略转换)。如果找不到,那么它将在 (ANamespace
).
中定义类型的命名空间中查找
Wikipedia 有一篇关于此的文章。
考虑一个简单的类型,在一个命名空间中,带有 operator==
:
namespace ANamespace {
struct Foo { int i; float f; };
}
#ifdef INSIDE
namespace ANamespace {
bool operator==(const Foo& l, const Foo& r)
{
return l.i == r.i && l.f == r.f;
}
}
#else
bool operator==(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l.i == r.i && l.f == r.f;
}
#endif
bool compareElements(const std::vector<ANamespace::Foo>& l, const std::vector<ANamespace::Foo>& r)
{
return l == r;
}
如果在 ANamespace
中定义了 operator==
(通过定义 INSIDE
),该示例将编译。但是,如果 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
,例如
bool compareDirectly(const ANamespace::Foo& l, const ANamespace::Foo& r)
{
return l == r;
}
似乎无论在哪里定义 operator==
都可以正常工作。
标准中是否有关于 STL 期望 operator==
定义的位置的规则?
!(*__first1 == *__first2)
发生在函数模板 std::operator==
中,因此它被认为是依赖的非限定函数调用表达式,因此在重载解析期间仅在 [=11= 的定义上下文中找到函数] 和那些通过 ADL 找到的是候选人。
很明显,在标准比较运算符的定义上下文中没有声明 operator==(const Foo&, const Foo&)
。在参数相关查找 (ADL) 中,检查每个参数的名称空间以搜索调用的可行函数,因此这就是为什么在 ANamespace
中定义 operator==
有效。
简而言之,在声明 class 的同一个命名空间中声明 operator==
可以保证依赖于参数的查找会找到它,所以这就是您应该做的。该标准并未强制要求您遵循此约定,但在实践中这是获得保证的唯一途径。这也适用于标准库可能对您的类型调用的其他运算符。
如果您选择在全局命名空间中声明 operator==
但您的类型是 而不是 在全局命名空间中声明,标准库算法有可能会仍然可以通过不合格的名称查找找到您的 operator==
。但是,不能保证这有效,因为不合格的名称查找将 停止 在找到 operator==
的最内层封闭命名空间。换句话说,在
namespace std {
template< class InputIt1, class InputIt2 >
constexpr bool equal( InputIt1 first1, InputIt1 last1,
InputIt2 first2 ) {
// ...
}
}
operator==
的非限定名称查找将找到在 std
命名空间中声明的任何 operator==
(当然,这不适用于您的用户定义类型)和然后,如果它在 std
中找到任何内容,即使它可能不是可行的重载,也不会在全局命名空间中查找。
您需要阅读 "ADL" 又名 "Argument Dependent Lookup"。
基本上,当您编写 v1 == v2
时,编译器会在当前命名空间中寻找带有两个正确类型 ANamespace::Foo
参数的 operator==
。 (注意:我们在这里忽略转换)。如果找不到,那么它将在 (ANamespace
).
Wikipedia 有一篇关于此的文章。