using directive和using declaration
// using directive int fun1(int i) { using namespace std; int vector = 7; // a poorly named variable, but it is legal. // vector a; // error. std::vector is hidden return vector * i; }
// using declaration int fun2(int i) { using std::vector; // int vector = 12; // error. redeclaration of 'vector'. vector a; // ok. return i * i; } int main(int argc, char *argv[]) { return 0; } |
对fun1中的代码,<>的解释是:
One interesting aspect of using directives is that they make the names of a namespace available, but as if they were declared at *global* scope, not necessarily at the scope in which the using directive occurs. Local names will hide namespace names.
所以fun1中名字vector只有一个可见的声明,就是那个int类型的变量。std名字空间中的模板类声明被隐藏了。所以试图用vector声明一个向量会出错。
在fun2中,using declaration的作用等于是在同样的位置,同样的作用域内声明了这个名字,因此试图在同一作用域内声明一个名叫vector的int类型变量的时候,会出现“重复声明”错误。
附:从上面的例子可以看出,通过using directive使用命名空间容易造成命名空间污染,因此google c++规范建议禁止使用using directive方式使用namespace,改用,例子中fun1改为
int fun1(int i) { namespace np_std = ::std; int vector = 7; np_std::vector a; return vector * i; } |
c++匿名命名空间与静态变量
当定义一个命名空间时,可以忽略这个命名空间的名称:
namespce { char c; int i; double d; } |
编译器在内部会为这个命名空间生成一个唯一的名字,而且还会为这个匿名的命名空间生成一条using指令。所以上面的代码在效果上等同于:
namespace __UNIQUE_NAME_ { char c; int i; double d; } using namespace __UNIQUE_NAME_; |
在匿名命名空间中声明的名称也将被编译器转换,与编译器为这个匿名命名空间生成的唯一内部名称(即__UNIQUE_NAME_)绑定在一起。还有一点很重要,就是这些名称具有internal链接属性,这和声明为static的全局名称的链接属性是相同的,即名称的作用域被限制在当前文件中,无法通过在另外的文件中使用extern声明来进行链接。如果不提倡使用全局static声明一个名称拥有internal链接属性,则匿名命名空间可以作为一种更好的达到相同效果的方法。
注:命名空间都是具有external 连接属性的,只是匿名的命名空间产生的__UNIQUE_NAME__在别的文件中无法得到,这个唯一的名字是不可见的。c++新的标准中提倡使用匿名命名空间,而不推荐使用static,因为static用在不同的地方,涵义不同,容易造成混淆,另外,static不能修饰class。
附:匿名命名空间与static功效等同。另外google c++关于匿名命名空间的规范建议不要在 .h 文件中定义命名空间。同时建议禁止定义静态储存周期非POD变量(函数作用域里的静态变量除外)。因为静态变量的构造函数、析构函数和初始化的顺序在 C++ 中是只有部分明确的,甚至随着构建变化而变化,可能导致不明确行为发生,难以发现的 bug。所以除了禁用类类型的全局变量,也不允许用函数返回值来初始化 POD 变量,除非该函数(比如 getenv() 或 getpid())不涉及任何全局变量。
实例:
//A.h class A{ public: void test_space(); };
//A.cpp namespace { int a=12; void testa(){ printf("(1) %d", a); } } void A::test_space(){ testa(); printf("(2) %d", a); }
int main(){ A a; a.test_space(); } |
附:
google c++规范建议,在类型定义中,类型转换运算符和单参数构造函数都应当用 explicit 进行标记。一个例外是,拷贝和移动构造函数不应当被标记为 explicit,因为它们并不执行类型转换。对于设计目的就是用于对其他类型进行透明包装的类来说,隐式类型转换有时是必要且合适的,这时应当联系项目组长并说明特殊情况。