命名空间混乱

时间:2013-04-18 11:08:43

标签: c++ namespaces

我是命名空间的新手,并且是从 C ++ Primer

尝试的
#include<iostream>
namespace Jill 
{
 double bucket;
 double fetch;
 struct Hill{ };
}

double fetch;

int main()
{
 using namespace Jill;
 Hill Thrill;
 double water = bucket; 
 //double fetch; //<<<<<<<<<<<<//
 std::cin>> fetch;
 std::cin>> ::fetch;
 std::cin>> Jill::fetch;
 std::cout<<"fetch is "<<fetch;
 std::cout<<"::fetch is "<< ::fetch;
 std::cout<<"Jill::fetch is "<< Jill::fetch;
}

int foom()
{
 Jill::Hill top;
 Jill::Hill crest;
}

当标记为//<<<<<<<<<<<<//的行未被注释时,我得到预期的结果。即 local变量会隐藏globalJill::fetch。但是当我发表评论时,剩下2个。 global fetchJill::fetch。并且编译器提供错误

namespaceTrial1.cpp:17:13: error: reference to ‘fetch’ is ambiguous
namespaceTrial1.cpp:9:8: error: candidates are: double fetch
namespaceTrial1.cpp:5:9: error:                 double Jill::fetch
namespaceTrial1.cpp:20:26: error: reference to ‘fetch’ is ambiguous
namespaceTrial1.cpp:9:8: error: candidates are: double fetch
namespaceTrial1.cpp:5:9: error:                 double Jill::fetch

我的问题是为什么编译器混淆这导致歧义?为什么不将fetch假设为Jill::fetch,因为我在using namespace Jill

的开头添加了main()

如果我在main的开头使用声明性using Jill::fetch;,问题就会得到解决。因为using Jill::fetch使它好像已在该位置声明。所以,它就像有一个local fetch变量。 [我是否正确?]为什么using declaration的行为就好像变量是在该位置声明而using directive没有?

4 个答案:

答案 0 :(得分:4)

当声明局部变量遮蔽全局/命名空间变量时,显式地告诉编译器。但是,当您使用using时,命名空间的变量实际上并不会在本地范围内结束。

从规范(第7.3.4节,第3点):

  

using-directive 不会将任何成员添加到它出现的声明区域。

另外(来自同一部分,第6点):

  

如果名称查找在两个不同的名称空间中找到名称的声明,并且声明不声明相同的实体并且不声明函数,则名称的使用是错误的。

答案 1 :(得分:3)

using namespace不会优先考虑导入的名称,从而导致您已经观察到的名称。

这里出现歧义错误的是语言设计决策:存在这样的优先级是相当危险的。想象一下,Jill命名空间是由几个开发人员维护的大型命名空间,可能来自不同的组织。您对其内容没有或只有有限的控制权,但仍然对其进行更改可能会默默地更改您的程序的含义。

答案 2 :(得分:3)

using指令以一种对大多数程序员来说并不完全直观的方式修改名称查找。标准在[namespace.udir] p2:

中说明了这一点
  

在非限定名称查找(3.4.1)期间,名称看起来好像是在最近的封闭命名空间中声明的,其中包含using-directive和指定的命名空间。

此措辞意味着命名空间中的名称不会出现在当前作用域中,而是出现在某些外部作用域中。在您的示例中,using指令位于全局命名空间中的函数中,而Jill也位于全局命名空间中,因此Jill中的名称看起来好像位于全局命名空间中。 (正如约阿希姆所说,这些名字实际上并没有在那里引入,因此它们不会立即与现有名称发生冲突,并且在实际使用时只会出现模糊的查找。)

这是一个简单的例子,编译器会给你一个错误,这很好。它实际上可能比这更复杂。

namespace Outer {
  namespace Mid1 { int i = 1; }
  namespace Mid2 {
    namespace Tricky {
      int i = 2;
      namespace Inner {
        void f() {
          using namespace Mid1;
          std::cout << i;
        }
      }
    }
  }
}

即使您在引用i的行旁边有using指令,也会输出2而不是1。但是包含Mid1和using指令的最近的封闭命名空间是Outer,因此Mid1::i的行为就像它在Outer中声明一样。如果你有一个Outer::i,它会被Tricky::i遮蔽,而Mid1::i则不会更好。

一个简单的解决方案是禁止使用指令并仅使用声明和命名空间别名。他们更直观。

答案 3 :(得分:2)

[basic.scope.declaration]表示局部变量在声明后隐藏全局

int j = 24;
int main() {
int i = j, j;
j = 42;
}
  

标识符j被声明为名称两次(并使用两次)。第一个j的声明区域包括整个示例。第一个j的潜在范围在j之后立即开始并延伸到程序的结尾,但其(实际)范围排除了和之间的文本。 j的第二个声明的声明性区域(紧接在分号之前的j)包括{和}之间的所有文本,但其潜在范围不包括i的声明。第二次声明j的范围与其潜在范围相同。

这解释了为什么声明局部变量时没有错误。

如果未声明局部变量,则会出现名称冲突。 fetch,并抛出错误。