- 现代C++编程实战:132个核心技巧示例(原书第2版)
- (罗)马里乌斯·班西拉
- 823字
- 2024-06-06 17:45:26
1.12.3 工作原理
内联命名空间的成员被视为周围命名空间的成员,这样的成员可以偏特化、显式实例化或显式特化。这是一个传递属性,这意味着如果命名空间A包含内联命名空间B,而B又包含内联命名空间C,那么C的成员是B和A的成员,而B的成员是A的成员。
为了更好地理解内联命名空间的作用,我们考虑这样一种情况:开发一个库,它会随着时间的推移从第一个版本发展到第二个版本(以及进一步发展),这个库在名为modernlib的命名空间下定义了所有的类型和函数。在第一个版本中,这个库看起来像这样:

库的客户端可以执行以下调用并返回值1:

然而,客户端可能决定像下面这样特化模板函数test():

在本例中,y的值不再是1,而是42,因为调用了用户特化的函数。
到目前为止,一切运行正常,但是作为库开发人员,你决定创建库的第二个版本,但仍同时发布第一个和第二个版本,并通过宏让用户决定选择使用哪个版本。在第二个版本中,你提供了test()函数的新实现,它不再返回1,而是返回2。为了能够同时提供第一个和第二个实现,你将它们放在名为version_1和version_2的嵌套命名空间中,并使用预处理器宏条件编译库:

出乎意料,客户端代码会崩溃,不管它使用的是库的第一个版本还是第二个版本。这是因为test函数现在在一个嵌套的命名空间中,foo的特化在modernlib命名空间中完成,但它实际上应该在modernlib::version_1或modernlib::version_2中实现。这是因为模板特化应在声明模板的同一命名空间中实现。
在这种情况下,客户端需要更改代码,如下所示:


这是一个问题,因为库暴露了实现细节,客户端需要了解这些细节以便进行模板特化。1.12.2节讲述了这些内部细节如何用内联命名空间隐藏。有了modernlib库的这个定义,在modernlib命名空间中客户端代码定义的特化test函数就不再会被破坏,因为当模板特化完成时,version_1::test()或version_2::test()(取决于客户端用的版本)都是外围modernlib命名空间的一部分。实现细节现在对客户端是隐藏的,客户端只能看到外围命名空间modernlib。
但是,你应该记住,命名空间std是为标准保留的,永远不应该内联。另外,如果命名空间在第一个定义中不是内联的,那么它就不应该被定义为内联的。