命名空间和运算符解析 [英] Namespaces and operator resolution

查看:159
本文介绍了命名空间和运算符解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这里有一个简单的例子来说明我的问题:

  #include< iostream> 

命名空间A
{
struct MyClass {};
}

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

命名空间B
{
struct MyClass {};

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

命名空间B
{
void Test()
{
std :: cout< A :: MyClass()<< std :: endl;
std :: cout<< B :: MyClass()<< std :: endl;
}
}

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

我得到以下错误:

 错误:没有匹配'operator'<<'in'std :: cout< A :: MyClass()'

注意,如果两个操作符都在命名空间内,都在全局命名空间,代码编译并正确执行。



我真的想了解发生了什么,以及什么好的实践



谢谢!

解决方案

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



它不搜索全局的原因大致如下。



从3.4 / 1开始:


...名称查找可以将超过
的一个声明与一个名称关联,如果
发现该名称是一个函数名;
声明被称为形成一个
的重载函数集(13.1)。
名称查找成功后发生
重载解决(13.3)。


编译器试图找到一个函数(你的操作符在这个上下文中),它首先尝试做名称查找以首先找到该函数。



现在从3.4.1 / 6开始:



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

N的封闭命名空间之一之前必须声明。


让我们分解一下。您在命名空间级别函数中使用运算符<< ,因此本节适用。它将尝试使用上述的优先级找到该运算符。您的操作符未在当前块或封闭块中声明(这指的是函数内的嵌套 {} )。然而,下一部分匹配...应在其在命名空间N ...中使用之前被声明。 实际上是在当前命名空间中的运算符< B ),因此它将该运算符添加到其匹配列表中。在 B 中没有任何更多的匹配,并且因为同名命名空间范围被认为是最好的匹配接近,它不会查找任何其他范围。 p>

将操作符放入命名空间A时,它的工作原因是因为要打印的项目是 A ,该命名空间实际上被认为是因为它包含在表达式的命名空间中。由于命名空间 A 认为它在该命名空间中找到合适的匹配项并进行正确编译。



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



一般来说,你应该有插入操作符命名空间作为它操作的类。


I am using a library that defines output stream operators (operator<<) in the global namespace. In my own namespace, I was always declaring such operators in the global namespace, and never had problems with them. But now for various reasons I need to declare those operators in my own namespace, and suddenly, the compiler can't seem to find the operators declared in the library anymore.

Here's a simple example that illustrates my problem:

#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;
}

I am getting the following error:

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

Note that if both operators are within the namespaces, or alternatively if they are both in the global namespace, the code compiles and executes correctly.

I'd really like to understand what's going on and also what the "good practice" for defining such operators with namespaces.

Thanks!

解决方案

Since Test is within namespace B the compile sees the operator in that namespace and notes that it doesn't have a matching signature. It also attempts to find the operator in namespace A which contains the class but can't find it there either. Because there's already such an operator (with the wrong signature) in namespace B it won't go and attempt to find one at global scope.

The reason it doesn't search for the global one is roughly as follows. I'm first going to quote the standard and then try to explain it.

From 3.4/1:

...Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded.

As I read this, when the compiler is trying to find a function (which your operators is in this context) it first tries to do name lookup to find the function first. Then next it tries to pick the right function from the set of overloads.

Now from 3.4.1/6:

A name used in the definition of a function(26) that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N or, if N is a nested namespace, shall be declared before its use in one of N’s enclosing namespaces.

Let's break this down. You're using the operator<< in a namespace level function so this section applies. It's going to try to find that operator using the precedence in the described above. Your operator isn't declared in the current block or enclosing blocks (this is referring to nested {} within your function). However the next part matches "...shall be declared before its use in namespace N...". There is in fact an operator<< in the current namespace (B) so it adds that operator to its list of matches. There aren't any more matches in B, and because same-namespace scope is considered the best possible match-closeness, it won't look into any other scopes.

The reason it works when you put the operator into namespace A is that since the item being printed is a member of A, that namespace is actually considered because it's included in the namespaces of the expression. Since namespace A is considered it finds the appropriate match in that namespace and compiles correctly.

Now that it has a list of possible operators, it tries to do overload resolution on them. Unfortunately the one in found in namespace B is the only one it considers and it doesn't match the required arguments.

In general you should have the insertion operators in the same namespace as the class upon which it operates.

这篇关于命名空间和运算符解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆