【问题标题】:operator<< overload resolution (C++)运算符<<重载决议(C++)
【发布时间】:2021-07-05 07:20:51
【问题描述】:

下面的代码在a::b::print 函数中给出错误:Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream&lt;char&gt;') and 'Foo')

如果我注释掉命名空间b 中的operator&lt;&lt; 重载,错误就会消失。但我不明白为什么这会有所不同,因为它与 namspace a 中的 operator&lt;&lt; 重载具有不同的签名。怎么回事?!

#include <iostream>

class Foo {};

namespace a {

  std::ostream &operator<<(std::ostream &os, Foo &foo);
  void print(Foo& foo) {
    std::cout << foo;
  }

  namespace b {
    std::ostream &operator<<(std::ostream &os, double d); // uncomment to resolve error
    void print(Foo& foo) {
      std::cout << foo; // error here
    }
  }
}

【问题讨论】:

    标签: c++ c++11 namespaces operator-overloading overload-resolution


    【解决方案1】:

    由于命名空间b 嵌套在命名空间a 中,b 中声明的名称将隐藏a 中声明的名称。当在a::b::print 中进行名称查找时,它会搜索b,并且如果找不到它要查找的内容,则继续在a 中搜索。所以它确实在b 中找到了运算符a 中的正确运算符。当您将其注释掉时,它在b 中找不到它,并继续搜索a 并找到它。您可以通过在命名空间 b 中添加它来解决此问题:

    using a::operator<<; 
    

    但是,您的代码的真正问题是您没有正确使用 ADL(依赖于参数的查找)。处理用户定义类型的操作符应该与它们操作的类型在相同的命名空间中。 ADL 所做的是添加额外的名称空间以进行搜索,以便与调用相关的任何参数(或模板参数)都将自动考虑其名称空间。 (这就是为什么当您的代码在 std 之外时,您可以将 std 命名空间中提供的运算符用于内置类型。)

    所以将operator&lt;&lt; 移出命名空间a 并进入Foo 所在的同一个(全局)命名空间。或者,可能更好,将Foo 移动到命名空间a

    【讨论】:

    • 非常感谢这位克里斯。我仍然不明白为什么在b 中找到operator&lt;&lt; 后名称查找停止? b 中的operator&lt;&lt; 没有std::cout &lt;&lt; foo 所需的正确签名,所以名称查找不应该忽略它,继续搜索吗?
    • 顺序是:1) 找到一个包含名称的范围,2) 从 (1) 中找到的所有名称构建一个重载集,然后 3) 选择最佳匹配(如果存在)。如果它一直在寻找匹配项,您可能会失去提供您自己的函数版本的能力,而只会产生歧义。出于同样的原因,其他人也可能会破坏您的代码。 (但 ADL 也受到库作者的诟病,因为它在不受欢迎的情况下也加入了,因为它可以在添加到其他名称空间的新函数存在时默默地改变你调用的内容——如果它们被添加并且恰好是更好的匹配。)
    猜你喜欢
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多