使用命名空间行为背后的基本原理

gez*_*eza 5 c++ namespaces language-lawyer

引用标准:

using 指令指定指定命名空间中的名称可以在 using 指令出现在 using 指令之后的范围内使用。在非限定名称查找 (3.4.1) 期间,名称看起来好像是在最近的封闭名称空间中声明的,该名称空间包含 using 指令和指定名称空间。

看看这段代码:

namespace A {

    int fn() { return 1; }

}

namespace Inner {

    int fn() { return 2; }

    namespace B {

        using namespace A;

        int z = fn();

    }

}
Run Code Online (Sandbox Code Playgroud)

在这里,在我知道命名空间的确切规则之前,我曾期望z将其初始化为 1,正如我所写的那样using namespace A,因此预计A::fn()将使用它。但事实并非如此,z将被初始化为2,Inner::fn()因为我引用的规则而被称为。

这种行为背后的基本原理是什么:“好像它们是在最近的包含 using 指令和指定命名空间的封闭命名空间中声明的”?

如果using namespace为该命名空间中的所有内容应用 using 声明,会有什么缺点?

注意:是促使我提出这个问题的相关问题。

rod*_*igo 5

命名空间系统的一个理想特性是我称之为增量 API 兼容性的特性。也就是说,如果我向命名空间添加一个符号,那么任何以前工作的程序都应该继续工作并表示相同的意思。

现在,带有重载的普通 C++ 不是渐进式 API 兼容的

int foo(long x) { return 1; }

int main()
{
    foo(0);
}
Run Code Online (Sandbox Code Playgroud)

现在我添加了重载int foo(int x) { return 2; },程序默默地改变了意义。

无论如何,当 C++ 人员设计namespace他们想要的系统时,在增加外部 API 时,以前的工作代码不应更改选择符号的名称空间。从您的示例中,以前的工作代码类似于:

namespace A {
    //no fn here, yet    
}

namespace Inner {

    int fn() { return 2; }

    namespace B {

        using namespace A;
        int z = fn();
    }
}
Run Code Online (Sandbox Code Playgroud)

并且z很容易初始化为2. 现在A用一个名为的符号扩充命名空间fn不会改变该工作代码的含义。

相反的情况并不真正适用:

namespace A {
    int fn() { return 1; }
}

namespace Inner {

    // no fn here

    namespace B {

        using namespace A;
        int z = fn();
    }
}
Run Code Online (Sandbox Code Playgroud)

这里z初始化为1. 当然,如果我添加fnInner它会改变程序的意义,但Inner不是外部API:实际上,当Inner最初写的,A::fn也已经存在(!它被称为),所以没有借口的人意识不到冲突。


一个有点实际的例子

想象一下这个 C++98 程序:

#include <iostream>

namespace A {
int move = 0;
void foo()
{
    using namespace std;
    cout << move << endl;
    return 0;
}
}

int main()
{
    A::foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我用 C++11 编译它,由于这个using规则,一切正常。如果using namespace std为该命名空间中的所有内容应用 using 声明,则该程序将尝试打印 functionstd::move而不是 A::move.