Operator< & lt;重载隐藏其他

Operator<< overloading hides other

本文关键字:隐藏 其他 重载 lt Operator      更新时间:2023-10-16

我有一个奇怪的运算符<<超载的问题,我找不到一个原因。下面的代码是该问题的摘录。这段代码在VS2015, g++ 5.4.0和clang 3.8.0下无法编译,所以我认为这不是编译器的错误。

#include <iostream>
#include <stdexcept>
inline std::ostream &operator<<(std::ostream &o, const std::exception &ex) {
    o << ex.what();
    return o;
}
namespace ns {
    struct Struct { int m; };
    inline std::ostream &operator<<(std::ostream &o, const Struct &s) {
        o << s.m;
        return o;
    }
    void fn() {
        std::cout << Struct{ 1 } << std::endl;
        try {
            throw std::runtime_error("...");
        } catch (std::exception &ex) {
            std::cout << ex << std::endl;
        }
    }
}
int main() {
    return 0;
}

编译器找不到操作符<<对于std::exception ("std::cout <<前女友& lt; & lt;std:: endl;"失败)。特别让我困惑的是,如果我:

  • 删除重载操作符<<用于Struct或
  • 如果我将所有代码从命名空间ns移动到全局命名空间

代码编译。这种行为的原因是什么?

您所看到的是c++中名称查找规则的结果。这就是为什么不建议在您编写的类类型中至少没有一个操作数的情况下提供操作符重载。

换句话说,你应该设计你的代码,使它没有operator<<(std::ostream&, std::exception),因为它会无意中被其他operator<<隐藏在命名空间中。

此行为对所有函数都是相同的,操作符名称也不例外。

另一种解决方案是将using ::operator<<;放在定义更多operator<<过载的任何名称空间中。


完整的名称查找规则集有点复杂。非限定函数名查找的要点是:

  1. 根据参数查找名称。
  2. 非adl查找查找名称。
  3. 将(1)和(2)的结果结合起来产生查找集。

在您的代码o << s.m中,步骤1在类std::ostream中搜索成员operator<<,在命名空间std中搜索非成员operator<<

步骤2搜索:

  • ns::operator<<的主体,以防在该函数中有任何函数声明!(没有找到)
  • 命名空间ns,直到并包括发生调用的函数点。(发现ns::operator<<)

关键是,一旦通过非adl查找找到ns::operator<<,非adl查找过程就会停止——它不会继续搜索更多的封闭命名空间来查找更多的重载。

最终查找集是(1)和(2)的并集,即std::ostream::operator<<std::operator<<ns::operator<<。然后重载解析只对这些限定名的重载进行。


下面是相同原理的一个更简单的例子,没有添加ADL集的干扰:

void bar(int);
namespace N
{
    void bar(char const *);
    void nfunc() { bar(1); }
}

没有ADL,因为参数1不是类类型。非adl非限定查找找到N::bar并停止。不考虑::bar。代码编译失败