5

我是命名空间的新手,正在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变量隐藏了globaland Jill::fetch。但是当我注释掉它时,还有 2 fetch left 。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()

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

4

4 回答 4

4

当你声明一个覆盖全局/命名空间变量的局部变量时,你明确地告诉编译器这一点。但是,当您使用using命名空间的变量时,实际上并没有在本地范围内结束。

根据规范(第 7.3.4 节,第 3 点):

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

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

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

于 2013-04-18T11:20:30.557 回答
3

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

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

于 2013-04-18T11:15:42.043 回答
3

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

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

这种措辞意味着来自命名空间的名称不会出现在当前范围内,而是出现在某个外部范围内。在您的示例中, using 指令位于全局命名空间中的函数中,而 Jill 也在全局命名空间中,因此 Jill 的名称看起来好像它们在全局命名空间中。(正如 Joachim 所说,这些名称实际上并没有在那里引入,因此它们不会立即与现有名称冲突,并且您只会在实际使用它们时得到模棱两可的查找。)

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

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

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

一个简单的解决方案是禁止 using 指令,只使用 using 声明和命名空间别名。它们更加直观。

于 2013-04-18T12:33:24.227 回答
2

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

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

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

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

如果未声明局部变量,则会发生名称冲突。编译器无法决定选择哪个fetch,并引发错误。

于 2013-04-18T11:26:08.093 回答