命名空间

每个库会定义大量的全局名字,而这不可避免的会发生冲突,放在全局命名空间中会造成命名空间污染。

而这时候就可以使用命名空间,来给每一个部分的名字加一个前缀来避免冲突。

定义

使用namespace name{}定义命名空间。可以定义于全局或者其他命名空间里,但不能再函数和类的内部。

每一个namespace就代表了一个作用域。

namespace可以被分开来几个部分定义,但只要声明了该namespace,相当于继续在这个namespace里定义,而不是新开一个。这种能够确保我们所需的函数和其他名字只定义一次。

一般在我们定义的namespace里include,因为这相当于将include里所有东西都放进这个namespace中。

对于一个在namespace中声明的成员,只能在namespace中或全局作用域中声明定义它,不能在不相关的作用域中定义它。

模板特例化的声明必须在原命名空间中,在此之后就可以在外面定义它了。

特殊的命名空间

全局命名空间,::variable。

对于嵌套的命名空间,内部的同名会隐藏外部的同名,因为命名空间就是作用域嘛,要访问就必须通过作用域符来限定。

内联命名空间,只能在第一次声明命名空间的时候进行声明,其中的名字可以被外层命名空间直接使用,一般用于版本控制,但是感觉直接using namespace也可以做到相同的事情。

未命名的命名空间,其作用域与该命名空间所在的命名空间一致,但很重要的一点是,它不能跨文件,而例如全局作用域可以通过extern来跨文件访问。

使用命名空间

通过namespace name1=name2可以为非常长的命名空间声明别名。

using声明,一次引入一个命名空间的成员,using namespace::name;可以用于全局作用域,局部作用域,命名空间作用域及类作用域中,但在类作用域中时只能指向基类成员。相当于在声明的地方声明了这个名字,当引入的是一个函数时,会引入这个函数的所有重载版本。

using指示,一次引入一整个命名空间,using namespace name;可用于全局、局部和命名空间作用域中。相当于在声明处所在的命名空间前声明了所有名字。

因此using指示可能会造成二义性,并且只有在使用这个二义性的名字的时候才会报错,因此尽量少用。

1
2
3
4
5
6
//相当于name1所有的名字在这里
fun{
using namespace name1;
//相当于name2在这里。
using namespace::name2;
}

类、命名空间与作用域

当调用一个作用域的函数时,不但会在调用处查找,还会在实参的命名空间查找,无论这个实参是引用还是指针,这样一些重载运算符才能找到对应的。

也正是因此,有的有形参的函数就能被查找到,而有的没有形参的就没能被查找到。

另外,由于move和forward都是只接受一个右值引用的,可以匹配任何类型,因此最容易引起冲突,因此最好都使用std::。

当声明一个友元的时候,实际上是将它声明在了最近的外层命名空间,和using指示一样,但是是隐式的,不与这个最近的外层命名空间发生关系,无法调用也不存在覆盖,只与内层的这个命名空间可能会产生覆盖关系。

重载与命名空间

正如前面提到的,由于会检查实参的作用域,因此一些原本不可见的函数也会变得可见,因此这些函数也会加入函数待选列表中。

当使用using声明引入同名函数时,要么覆盖外层同参同名并扩充函数列表,要么与局部作用域的同名同参函数发生重定义的问题。

当使用using指示引入同名函数时,最多也只会发生二义性错误,因此使用时指定即可。


命名空间
https://lhish.github.io/project/hide/命名空间/
作者
lhy
发布于
2024年6月30日
许可协议