命名空间和操作员解析

cha*_*ign 37 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;
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’
Run Code Online (Sandbox Code Playgroud)

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

我真的很想了解发生了什么,以及用命名空间定义这些运算符的"好习惯".

谢谢!

Mar*_*k B 37

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

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

从3.4/1:

...如果名称查找名称是函数名称,则名称查找可以将多个声明与名称相关联; 据说声明形成一组重载函数(13.1).名称查找成功后,将发生重载分辨率(13.3).

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

现在从3.4.1/6:

在函数(26)的定义中使用的名称是名称空间N的成员(其中,仅出于说明的目的,N可以表示全局范围),在使用它之前的块中应该被声明.或者在其中一个封闭块(6.3)中,或者在其在命名空间N中使用之前声明,或者,如果N是嵌套命名空间,则应在其在N的封闭命名空间之一中使用之前声明.

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

将操作符放入命名空间A时它起作用的原因是,由于正在打印的项是其成员A,因此实际考虑该命名空间,因为它包含在表达式的名称空间中.由于名称空间A 认为是在该名称空间中找到适当的匹配并正确编译.

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

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

  • 这个答案解释了这个问题,但没有解决.解决方案是在`namespace B`中使用`using :: operator <<;`,请参阅http://stackoverflow.com/a/5196150/1915854 (4认同)

Lou*_*Lou 11

我的答案与其他答案非常相似,但特别是,编译器试图找到A :: operator <<(),因为它在A命名空间中运行.如果要调用命名空间外的那个,可以使用显式调用它

::operator<<(std::cout, A::MyClass();
Run Code Online (Sandbox Code Playgroud)

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


Ser*_*tch 9

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

using ::operator<<;
Run Code Online (Sandbox Code Playgroud)

在OP的代码示例的代码行会去沿着宣布其它代码位置/定义operator<<namespace B:

namespace B
{
   struct MyClass {};

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

   using ::operator<<;
}
Run Code Online (Sandbox Code Playgroud)


Mor*_*sen 1

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

  • “请注意,如果两个运算符都在命名空间内,或者如果它们都在全局命名空间内,则代码可以正确编译和执行。” 他正在寻找它不起作用背后的理由;他已经知道如何解决它。 (3认同)