用于大型程序的工具
--命名空间[续3]
六、重载与命名空间
正如我们所见,每个命名空间维持自己的作用域,因此,作为两个不同命名空间的成员的函数不能互相重载。但是,给定命名空间可以包含一组重载函数成员。
1、候选函数与命名空间
命名空间对函数匹配有两个影响。一个影响是明显的:using声明或using 指示可以将函数加到候选集合。另一个影响则微妙得多。
正如前节所见,有一个或多个类类型形参的函数的名字查找包括定义每个形参类型的命名空间。这个规则还影响怎样确定候选集合,为找候选函数而查找定义形参类(以及定义其基类)的每个命名空间,将那些命名空间中任意与被调用函数名字相同的函数加入候选集合。即使这些函数在调用点不可见,也将之加入候选集合:
namespace NS { class Item_base { //... }; void display(const Item_base &) { //... } } class Bulk_item :public NS::Item_base { //... }; int main() { Bulk_item book; display(book); //OK }
解释:display调用的候选函数不仅是在调用display函数的地方其声明可见的函数,还包括声明 Bulk_item类及其基类Item_base的命名空间中的函数。命名空间NS中声明
的函数display(constItem_base&) 被加到候选函数集合中。
2、重载与using声明
using声明声明一个名字。没有办法编写using声
明来引用特定函数声明:
using NS::print(int); //Error using NS::print; //OK
如果命名空间内部的函数是重载的,那么,该函数名字的using声明声明了所有具有该名字的函数。
由using声明引入的函数,重载出现using声明的作用域中的任意其他同名函数的声明。
如果using声明在已经有同名且带相同形参表的函数的作用域中引入函数,则using声明出错,否则,using定义给定名字的另一重载实例,效果是增大候选函数集合。
3、重载与using指示
using指示将命名空间成员提升到外围作用域。如果命名空间函数与命名空间所在的作用域中生命的函数同名,就将命名空间成员加载到重载集合中:
namespace libs_R_us { extern void print(int); extern void print(double); } void print(const std::string &); using namespace libs_R_us; void fooBar(int val) { print("Value: "); print(val); }
4、跨越多个using指示的重载
如果存在许多using指示,则来自每个命名空间的名字成为候选集合的组成部分:
namespace AW { int print(int); } namespace Primer { double print(double); } using namespace AW; using namespace Primer; long double print(long double); int main() { print(1); //AW::print(int) print(3.1); //Primer::print(double) }
全局作用域中print函数的重载集合包含函数print(int)、print(double)和 print(longdouble),即使这些函数原来在不同的命名空间中声明,它们都是为main中函数调用考虑的重载集合的组成部分。
//P614 习题17.22 //1 namespace primerLib { void compute(); void compute(const void *); } using primerLib::compute; void compute(int) { cout << "int" << endl; } void compute(double,double = 3.14); void compute(char *,char * = 0); int main() { compute(0); //compute(int) }
//2 namespace primerLib { void compute(); void compute(const void *) { cout << "const void *" << endl; } } void compute(int); void compute(double,double = 3.14); void compute(char *,char * = 0); int main() { using primerLib::compute; compute(0); //compute(const void *) }
七、命名空间与模板
在命名空间内部声明模板影响着怎样声明模板特化:模板的显式特化必须在定义通用模板的命名空间中声明,否则,该特化将与它所特化的模板不同名。
有两种定义特化的方式:一种是重新打开命名空间并加入特化的定义,可以这样做是因为命名空间定义是不连续的;或者,可以用与在命名空间定义外部定义命名空间成员相同的方式来定义特化:使用由命名空间名字限定的模板名定义特化。
【小心地雷】
为了提供命名空间中所定义模板的自己的特化,必须保证在包含原始模板定义的命名空间中定义特化。