C++ 使用指令——为什么会有如此奇怪的行为?

C++ 使用指令——为什么会有如此奇怪的行为?,c++,C++,我有这个示例代码 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: er

我有这个示例代码

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中?

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

如果确实要在没有限定ID的情况下调用函数,可以使用
声明使其可见:

namespace ns1 {
    void foo(int) { }
}

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

ns1::foo
ns2::foo

来自N3337,§3.3.10/1[基本范围隐藏]

在嵌套的声明性区域或派生类(10.2)中,名称可以通过该名称的显式声明隐藏

您引用的章节(§7.3.4/2)后面紧跟着

3 using指令不会向其出现的声明性区域添加任何成员。[示例:

-[结束示例]

在您的例子中,using指令将
ns1
中的名称引入到它出现的位置的公共祖先名称空间,以及
ns1
的名称空间,即全局名称空间。它在
ns2
中不引入它们,因此
ns2::foo
的声明隐藏了
ns1::foo
的声明

如果希望找到
ns1::foo
,请改用using声明

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

因为我从未见过“使用”这样使用,所以我大胆猜测一下。它尝试将名称拉入当前名称空间,可能不会像您希望的那样仅拉入函数上下文。这两个函数的冲突可能是一个问题。您可以给编译器一个选择,ns1::foo和ns2::foo都在范围内。你认为它应该选哪一个?最近的还是最远的?您喜欢编译器选择与函数参数同名的全局变量吗?如果你说“ns1::foo当然!”那么为什么不直接这样写呢?根据D&E的说法,使用指令“使名称可访问”。由于我们(可能)不知道可访问的完整名称集,因此它更为保守,并将它们放在命名空间层次结构的更高位置(因此它们不会意外地隐藏名称)。最近的封闭名称空间可能被解释为程序的公共部分;任何封闭的东西都不太专业,任何封闭的东西都更专业,因此应该优先考虑。但我只是猜测。我知道使用声明的可能性,但问题是关于使用指令。@maxim.yurchuk:是的——这就是为什么我首先回答了关于使用指令的问题,我只做了一次,就添加了关于一种可能的解决方法的信息。关于减少冲突和意外:我认为这个例子:是相当令人惊讶的。我知道这些事实,我理解为什么这段代码不能编译。我问了关于这个规则的解释:为什么使用声明会在范围中引入名称,而使用指令则不会?@maxim.yurchuk这是对规则的解释。如果你在寻找他们为什么这样工作的基本原理,那么JerryCoffin的答案可能就是你在寻找的答案。我不知道还有其他原因。
void bar()
{
    using ::ns1::foo;
    foo(42);
}