为什么 ADL 的运算符函数行为与其他函数不同?

Why ADL has a different behavior for operator function than other functions?

本文关键字:函数 其他 ADL 运算符 为什么      更新时间:2023-10-16

我以这种方式在NS_C命名空间中创建了一个C类:

#include <iostream>
namespace NS_C {
template <typename T>
class C {
public:
C operator+(long) {
std::cout << "NS_C::C::operator+n";
return *this;
}
void not_operator(C<T>, long) {
std::cout << "NS_C::C::not_operatorn";
}
void call() {
*this + 0;
not_operator(*this, 0);
}
};
}

函数call应该调用NS_C::C::operator+然后NS_C::C::not_operator。为了测试此行为,我运行了这个小程序:

int main()
{
NS_C::C<int> ci;
ci.call();
return 0;
}

输出是我期望的:

> g++ -o example example.cpp && ./example
NS_C::C::operator+
NS_C::C::not_operator

现在,我想在单独的命名空间NS_A中创建一个新的类A,并向此命名空间添加两个operator+not_operator函数的泛型重载:

#include <iostream>
namespace NS_A {
class A {};
template <typename T>
T operator+(T t, int)
{
std::cout << "NS_A::operator+n";
return t;
}
template <typename T>
void not_operator(T, int)
{
std::cout << "NS_A::not_operatorn";
}
}

多亏了 ADL,从NS_C::C<NS_A>对象调用call成员函数将调用重载的NS_A::operator+,因为它匹配得更好(第二个参数在NS_A::operator+int,在NS_C::C::operator+long(。 但是,我不明白为什么我的not_operator函数不会发生相同的行为。实际上,NS_C::C::not_operator仍将从call函数中调用。

让我们使用以下主函数:

int main()
{
NS_C::C<NS_A::A> ca;
ca.call();
return 0;
}

我有以下输出:

NS_A::operator+
NS_C::C::not_operator

为什么在这种情况下不调用NS_A::not_operator


以下是重现该问题的完整代码:

#include <iostream>
namespace NS_A {
class A {};
template <typename T>
T operator+(T t, int)
{
std::cout << "NS_A::operator+n";
return t;
}
template <typename T>
void not_operator(T, int)
{
std::cout << "NS_A::not_operatorn";
}
}
namespace NS_C {
template <typename T>
class C {
public:
C operator+(long) {
std::cout << "NS_C::C::operator+n";
return *this;
} 
void not_operator(C<T>, long) {
std::cout << "NS_C::C::not_operatorn";
}
void call() {
*this + 0;
not_operator(*this, 0);
}
};
}   
int main()
{
NS_C::C<int> ci;
ci.call();
NS_C::C<NS_A::A> ca;
ca.call();
return 0;
}

来自 overload_resolution#Call_to_an_overloaded_operator:

我们有重载运算符的候选重载集:

1( 成员候选:如果 T1

是一个完整的类或当前正在定义的类,则成员候选集是 T1::operator@ 的限定名称查找的结果。在所有其他情况下,成员候选项集为空。

2( 非成员候选:对于运算符重载允许非成员窗体的运算符,在表达式上下文中查找operator@的非限定名称找到的所有声明(可能涉及 ADL(,但成员函数声明将被忽略,并且不阻止查找继续进入下一个封闭范围。如果二元运算符的操作数或一元运算符的唯一操作数都具有枚举类型,则查找集中成为非成员候选项的唯一函数是其参数具有该枚举类型(或对该枚举类型的引用(的函数

而对于另一个,我们只有unqualified_lookup

unqualified_lookup#中甚至还有一个例子Overloaded_operator 显示operator+(a, a)a + a之间的差异

相关文章: