【问题标题】:using directive -- why so strange behaviour?using 指令——为什么会有这么奇怪的行为?
【发布时间】:2014-12-11 22:23:28
【问题描述】:

我有这个示例代码

namespace ns1
{
    void foo(int)
    {
    }
}

namespace ns2
{
    void foo()
    {
    }
    void bar()
    {
        using namespace ::ns1;
        foo(42); // why compiler can't just call ns1::foo?
    }
}

而且编译时不会出错:

prog.cpp:16:9: error: too many arguments to function ‘void ns2::foo()’

我在 C++ 2003 标准中找到了这个错误的原因:

using 指令指定指定命名空间中的名称 可以在 using 指令出现的范围内使用 使用指令。在非限定名称查找 (3.4.1) 期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 其中包含 using 指令和指定的命名空间。 [注:在此上下文中,“包含”是指“直接包含或 间接”。 ]

这个奇怪的规则有什么理由吗?为什么命名空间 ns1 中的名称不能直接出现在命名空间 ns2 中?

【问题讨论】:

  • 由于我从未见过以这种方式使用“使用”,所以我冒险猜测一下。它尝试将名称拉入当前命名空间,并且可能不像您打算仅拉入函数上下文那样。那么这两个函数的冲突可能是一个问题。
  • 你给编译器一个选择,ns1::foo 和 ns2::foo 都在范围内。你觉得应该选哪一个?是最近的还是最远的?当编译器选择一个与函数参数同名的全局变量时,你喜欢吗?如果你说“ns1::foo 当然!”那为什么不这样写呢?
  • 根据 D&E,使用指令“使名称可访问”。由于我们(可能)不知道可以访问的完整名称集,因此它更加保守,并将它们放在命名空间层次结构中的更高级(因此它们不会意外隐藏名称)。最近的封闭命名空间可能被解释为程序的公共部分;任何封闭的东西都不那么专业,任何封闭的东西都更专业,因此应该优先考虑。但我只是猜测。

标签: c++


【解决方案1】:

我相信这样做是为了减少冲突和意外。由using 指令带入作用域的名称是可见的,但直接包含在本地作用域中的任何内容都将优先于它。

如果你真的想在没有限定 ID 的情况下调用你的函数,你可以使用 using 声明让它可见:

namespace ns1 {
    void foo(int) { }
}

namespace ns2 {
    void foo() { }
    void bar() {
        using ::ns1::foo;
        foo(42); // No problem; calls ::ns1::foo.
    }
}

【讨论】:

  • 我知道使用 using 声明的可能性,但问题正是关于 using 指令。
  • @maxim.yurchuk:是的——这就是为什么我首先回答了关于using 指令的问题,并且只有在我完成后才添加了有关一种可能的解决方法的信息。
  • 关于减少冲突和意外:我认为这个例子:ideone.com/qdGaCC 相当令人惊讶。
【解决方案2】:

ns1::foons2::foo 的声明隐藏了

来自 N3337,§3.3.10/1 [basic.scope.hiding]

可以通过在嵌套声明区域或派生类 (10.2) 中显式声明相同名称来隐藏名称。

您引用的部分 (§7.3.4/2) 紧随其后

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

    namespace A {
      int i;
      namespace B {
        namespace C {
          int i;
        }
        using namespace A::B::C;
        void f1() {
          i = 5; // OK, C::i visible in B and hides A::i
        }
      }
      // more (irrelevant) stuff
    }

——结束示例]

在您的情况下,using-directivens1 中的名称引入它出现的共同祖先命名空间,以及 ns1 的名称,这意味着全局命名空间。它没有ns2 中引入它们,所以ns2::foo 的声明隐藏了ns1::foo 的声明。

如果您想找到ns1::foo,请改用 using 声明。

void bar()
{
    using ::ns1::foo;
    foo(42);
}

【讨论】:

  • 我知道这些事实并且我理解为什么这段代码不能编译。我问了这个规则的解释:为什么使用声明在范围内引入名称,而使用指令没有?
  • @maxim.yurchuk 这是规则的解释。如果您正在寻找它们为何以这种方式工作的基本原理,那么 JerryCoffin 的答案可能就是您要寻找的答案。我不知道有任何其他原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 2015-03-02
  • 2015-05-31
  • 2015-08-01
相关资源
最近更新 更多