unnamed namespace and using declaration
在工作中经常看到这样的代码。好奇为什么要用unnamed namespace
,以及在unnamed namespace
里用using
会有什么效果。
1 | namespace NS_A1 { |
unnamed namespace scope
unnamed namespace不同于常规的namespace之处
首先想搞明白的就是unnamed namespace
的scope(作用域)。
unnamed namespace
只作用在当前的文件,也就是说如果两个cpp文件里都有unnamed namespace
,那么他们没有任何关联。
如果unnamed namespace
是定义在头文件里,那么引用头文件的cpp文件用到的unnamed namespace
里的成员也是仅属于当前的cpp文件。也就是说,比如有两个cpp文件都用到头文件unnamed namespace
的某个变量a
,那么相当于他们都创建了只能在自己cpp文件使用的a
变量,各种操作当然只会对自己的a
起作用。
示例: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// main.cpp
int main()
{
std::cout<<"in main, before change, a: " << a << "\n";
func();
a = 1;
std::cout<<"in main, after change, a: " << a << "\n";
func();
return 0;
}
// nsInHeader.h
namespace {
int a = 0;
}
// func.cpp
void func() {
std::cout << "in func, a: " << a << "\n";
}
// func.h
void func();
运行输出结果:
1 | in main, before change, a: 0 |
定义在unnamed namespace里的成员的scope
定义在unnamed namespace
里的成员跟这个unnamed namespace
本身的scope相同。
1
2
3
4
5
6
7
8// C++ primer例子,英文第五版p791
int i; // global declaration for i
namespace {
int i;
}
// ambiguous: defined globally and in an unnested, unnamed namespace
i = 10;unnamed namespace
定义在一个文件的最外层scope,相当于定义在这个文件的global
scope,那unnamed namespace
的成员也相当于定义在global
scope。
这也是为什么unnamed namespace
能够取代static变量。定义在global
scope的unnamed namespace
里的成员跟用static声明的变量都只在当前文件里可用,对其他文件不可见。
通过例子看普通namespace和unnamed namespace的区别
分别在两个cpp文件里定义namespace NS1,并在namespace里定义一个变量:
1
2
3
4
5
6
7
8
9
10// ns1.cpp
namespace NS1 {
int a = 0;
}
// main.cpp
// reopen the namespace NS1:
namespace NS1 {
int a = 0;
}
但是如果改成下面这样: 1
2
3
4
5
6
7
8
9
10
11
12
13
14// ns1.cpp
namespace NS1 {
namespace{
int a = 0;
}
}
// main.cpp
// reopen the namespace NS1:
namespace NS1 {
namespace{
int a = 0;
}
}
这是因为普通的namespace可以展开到多个文件,但是就如之前所说,unnamed namespace只在当前的文件有效。
Using
把别的namespace的成员引入当前的namespace,有两种using的用法:using declaration和using Directives。 这里用到的是using declaration所以就只讨论它。关于using directive,我发现了一篇很好的文章,放在后面了。
using declaration
In namespace and block scope
Using-declarations introduce a member of another namespace into the current namespace or block scope.
https://en.cppreference.com/w/cpp/language/using_declaration
之前提到的using NS_B1::NS_B2::NS_B3::ClassB;
就属于using declaration
。它只是把其它namespace的成员引入到当前的scope。
而“定义在unnamed namespace
里的成员跟这个unnamed namespace
本身的scope相同”。
也就是说,其实using declaration
放不放在unnamed namespace
里,效果都是一样的。
那么我猜在工作中看到using declaration
放在unnamed namespace
只是顺便;因为unnamed namespace
里还定义了别的变量和函数,这些成员只在当前文件可见;别的文件如果用了相同的enclosing namespace
的,也定义了类似的变量和函数,放在unnamed namespace
里就不会导致命名冲突。
include
#include < h-char-sequence > new-line (1)
#include " q-char-sequence " new-line (2)
Searches for a header identified uniquely by h-char-sequence and replaces the directive by the entire contents of the header.
Searches for a source file identified by q-char-sequence and replaces the directive by the entire contents of the source file. It may fallback to (1) and treat q-char-sequence as a header identifier.
https://en.cppreference.com/w/cpp/preprocessor/include
在preprocessor处理cpp文件的时候,include directive会把头文件的全部内容拿过来。
Translation Unit
一个translation unit就是一个cpp文件和它引用的头文件。 https://stackoverflow.com/questions/1106149/what-is-a-translation-unit-in-c
所以,如果不把using declaration
放在头文件里并被另一个cpp文件引用的话,这个using declaration
对于另一个cpp文件来说就是不可见的。
using Directives
https://quuxplusone.github.io/blog/2020/12/21/using-directive/
这篇文章说用using directive会把target namespace的成员引入到target namespace和current scope的least common ancestor的scope里。
而且这个引入的效果仅限在current scope(以及它包含的scope)里。在外层和其他的的scope里,target namespace的成员依然是不可见的。
而且最后的建议就是不要用using Directives。