命名空间和操作员解析

时间:2011-03-04 15:12:35

标签: c++ namespaces operator-keyword

我正在使用一个库来定义全局命名空间中的输出流操作符(operator<<)。在我自己的命名空间中,我总是在全局命名空间中声明这样的运算符,并且从未遇到过这些运算符的问题。但是现在出于各种原因我需要在我自己的命名空间中声明这些运算符,突然之间,编译器似乎无法找到库中声明的运算符。

这是一个简单的例子,说明了我的问题:

#include <iostream>

namespace A
{
   struct MyClass {};
}

std::ostream & operator<<( std::ostream & os, const A::MyClass & )
   { os << "namespace A"; return os; }

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }
}

namespace B
{
   void Test()
   {
      std::cout << A::MyClass() << std::endl;
      std::cout << B::MyClass() << std::endl;
   }
}

int main()
{
   B::Test();
   return 1;
}

我收到以下错误:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’

请注意,如果两个运算符都在命名空间内,或者如果它们都在全局命名空间中,则代码会正确编译并执行。

我真的很想了解正在发生的事情以及使用命名空间定义此类运算符的“良好做法”。

谢谢!

4 个答案:

答案 0 :(得分:35)

由于Test在命名空间B内,编译会看到该命名空间中的运算符,并注意到它没有匹配的签名。它还试图在名称空间A中找到包含该类但在其中找不到它的运算符。因为在命名空间B中已经存在这样的运算符(带有错误的签名),所以它不会去尝试在全局范围内找到一个。

它不搜索全局的原因大致如下。我首先要引用标准,然后尝试解释它。

从3.4 / 1:

  

...名称查找可以关联多个   如果有一个名称的声明   找到名称作为函数名称;   据说声明形成了一个   一组重载函数(13.1)。   发生过载分辨率(13.3)   名称查找成功后。

当我读到这篇文章时,当编译器试图找到一个函数(你的运算符在这个上下文中)时,它首先尝试进行名称查找以首先找到函数。接下来它会尝试从重载集合中选择正确的函数。

现在从3.4.1 / 6开始:

  

a的定义中使用的名称   作为成员的函数(26)   命名空间N(其中,仅用于   博览会的目的,N可以   代表全球范围)   在块中使用之前声明   在其中使用或在其中一个   封闭块(6.3)或应该是   在命名空间N中使用之前声明   或者,如果N是嵌套命名空间,则应   在其中一个之前被宣布   N是封闭的命名空间。

让我们打破这个。您在命名空间级别函数中使用operator<<,因此本节适用。它将尝试使用上述优先级找到该运算符。您的运算符未在当前块或封闭块中声明(这是指函数中的嵌套{})。但是,下一部分匹配“......应在名称空间N ...中使用之前声明”。 实际上是当前命名空间中的operator<<B),因此它将该运算符添加到其匹配列表中。 B中没有更多匹配项,并且因为相同命名空间范围被认为是最佳匹配关系,所以它不会查看任何其他范围。

将运算符放入命名空间A时它起作用的原因是,由于正在打印的项目是A的成员,因此实际考虑该命名空间是因为它包含在表达式的名称空间中。由于名称空间为A ,因此它会在该名称空间中找到相应的匹配项并正确编译。

现在它有一个可能的运算符列表,它会尝试对它们进行重载解析。不幸的是,命名空间B中找到的那个是它考虑的唯一一个,它与所需的参数不匹配。

通常,您应该将插入运算符放在与其运行的类相同的命名空间中。

答案 1 :(得分:10)

我的答案与其他答案非常相似,但特别是,编译器正在尝试查找A :: operator&lt;&lt;(),因为它正在A命名空间中运行。如果要调用命名空间外的那个,可以使用

显式调用它
::operator<<(std::cout, A::MyClass();

为了更顺畅地使用语法,请将其放在命名空间中。

答案 2 :(得分:8)

@Mark B在答案中解释了这个问题。 以下解决了这个问题。在要使用全局operator<<的命名空间中,键入以下代码:

using ::operator<<;

在OP的代码示例中,该行代码将转到其他代码的位置,该代码为operator<<声明/定义namespace B

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }

   using ::operator<<;
}

答案 3 :(得分:0)

这是因为您的第一个operator<<()是在命名空间A之外定义的。