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

时间:2017-07-04 18:36:52

标签: c++ namespaces language-lawyer

引用标准:

  

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

看看这段代码:

namespace A {

    int fn() { return 1; }

}

namespace Inner {

    int fn() { return 2; }

    namespace B {

        using namespace A;

        int z = fn();

    }

}

在此之前,在我了解命名空间的确切规则之前,我曾预计z将被初始化为1,因为我写了using namespace A,因此预计会使用A::fn()。但事实并非如此,z将被初始化为2,因为Inner::fn()因为我引用的规则而被调用。

这种行为背后的理由是什么:“好像它们是在最近的包含命名空间中声明的,它包含using-directive和指定的命名空间”?

如果using namespace使用声明来处理该命名空间中的所有内容,那将是什么缺点?

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

1 个答案:

答案 0 :(得分:4)

命名空间系统的理想属性是我称之为增量API兼容性的属性。也就是说,如果我在命名空间中添加一个符号,那么任何以前工作的程序都应该继续工作并且意味着同样的事情。

现在,带有重载的普通C ++不是增量API兼容

int foo(long x) { return 1; }

int main()
{
    foo(0);
}

现在我添加了重载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();
    }
}

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

相反的情况并不适用:

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

namespace Inner {

    // no fn here

    namespace B {

        using namespace A;
        int z = fn();
    }
}

此处z已初始化为1。当然,如果我将fn添加到Inner,它将更改程序的含义,但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;
}

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