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

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

我已经在 NS_C 命名空间中创建了一个 C class:

#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_operator\n";
      }

      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 中创建一个新的 class A 并向该命名空间添加 operator+ 和 [=25 的两个通用重载=] 函数:

#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_operator\n";
  }
}

感谢 ADL,从 NS_C::C<NS_A> 对象调用 call 成员函数将调用重载的 NS_A::operator+,因为它更匹配(第二个参数是 intNS_A::operator+longNS_C::C::operator+)。 但是,我不明白为什么我的 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_operator\n";
  }
}

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_operator\n";
      }

      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) member candidates: if T1 is a complete class or a class currently being defined, the set of member candidates is the result of qualified name lookup of T1::operator@. In all other cases, the set of member candidates is empty.

2) non-member candidates: For the operators where operator overloading permits non-member forms, all declarations found by unqualified name lookup of operator@ in the context of the expression (which may involve ADL), except that member function declarations are ignored and do not prevent the lookup from continuing into the next enclosing scope. If both operands of a binary operator or the only operand of a unary operator has enumeration type, the only functions from the lookup set that become non-member candidates are the ones whose parameter has that enumeration type (or reference to that enumeration type)

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

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

之间的差异