C++ 匿名namespace的作用以及它与static的区别

一、匿名namespace的作用
在C语言中,如果我们在多个tu(translation unit)中使用了同一个名字做为函数名或者全局变量名,则在链接阶段就会发生重定义错误,为了解决这个问题,我们可以在定义这些标识符(identifier)的时候加上static关键字修饰以限制它只在一个tu范围内可见。
C++继承了C语言中static关键字的这个用途,我们依旧可以使用static来避免多个tu中使用同一个标识符带来的重定义问题。此外C++还提供了另一种特有的方式,那就是匿名namespace:一个没有指定名字的namespace被称为一个匿名namespace;在一个tu中可以出现多个匿名namespace,并且相同层次的匿名namespace实际上被合成为同一个;出现在不同tu的匿名namespace中的相同标识符相互独立不会发生冲突,因此我们可以把那些只希望在同一个tu范围可见的全局标识符放入一个匿名namespace中,效果与前面加static相同。

二、匿名namespace与static的区别
一个全局标识符被static修饰后它的linkage变为internal linkage,这就是为什么不同tu中的相同标识符不会发生冲突的原因。
而匿名namespace却并不会改变在它内部定义的标识符的linkage,它用来避免名字冲突所采用的手段同C++用来实现重载的手段一摸一样,就是使用名字改编(name mangling):根据C++标准7.3.1.1,每个tu中的匿名namespace实际上会拥有一个独一无二的名字,因此在不同tu的匿名namespace中相同的标识符实际上属于不同的namespace,自然在名字改编后就不会发生冲突了:

7.3.1.1 Unnamed namespaces [namespace.unnamed]
An unnamed-namespace-definition behaves as if it were replaced by
        namespace unique { /* empty body */ }
        using namespace unique;
        namespace unique { namespace-body }
where all occurrences of unique in a translation unit are replaced
by the same identifier and this identifier differs from all other
identifiers in the entire program.

为什么匿名namespace不采取跟static一样的做法呢,搞个新花样岂不是增加了编译器开发的负担?这其实是因为另一个C++的特性牵制了匿名namespace的实现,那就是模板非类型参数(template non-type arguments):

14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter
shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including
   function templates and function template-ids but excluding non-static
   class members, expressed as & id-expression where the & is optional
   if the name refers to a function or array, or if the corresponding
   template-parameter is a reference; or
— a pointer to member expressed as described in 5.3.1 .

正是被红字标出的external linkage这一需求限制了匿名namespace的实现!试想一下,假如我们有一个全局对象或者函数只希望它在一个tu中有效,又希望能够用它的地址来实例化一个模板,怎么办?只在一个tu中有效,可以选择internal linkage,但是要用它的地址做为模板参数,又要求它必须要是external linkage!!
很显然,匿名namespace不改变其内部标识符的linkage这一性质解决了这一难题,我们可以把这个全局对象或者函数放心的扔在一个匿名namespace中,然后用它的地址来实例化一个模板,绝对不会发生重定义错误:)

现在大部分C++书籍都认为匿名namespace和static是相同的,而正如这里所阐述的,它们之间差异是明显的:static修饰的标识符由于internal linkage的限制,是不能用来实例化模板的!

最后给出一个例子证实匿名namespace确实不改变linkage,呵呵
代码中验证了external linkage/internal linkage/no linkage三种情况
-------------------------------------------------------------------------------

template <char *p>
struct foo
{
    void bar();
};
static char a =‘a‘;
namespace
{
    char b = ‘b‘;
    static char c = ‘c‘;
    template <class T> struct xxx {};
    void foobar()
    {
        struct no_linkage {};
        xxx<no_linkage>();  // 如果编译错误,说明no_linkage的linkage没有变化
    }
}
int main()
{
    foo<&a>().bar();  // 由于a的linkage是internal,因此应该编译错误
    foo<&b>().bar();  // 如果编译正确,说明b的linkage是external
    foo<&c>().bar();  // 如果编译错误,说明c的linkage是internal
    foobar();
    return 0;
}

-------------------------------------------------------------------------------
Comeau C/C++ 4.3.3 (Aug  6 2003 15:13:37) for ONLINE_EVALUATION_BETA1
Copyright 1988-2003 Comeau Computing.  All rights reserved.
MODE:strict errors C++

"ComeauTest.c", line 19: error: a template argument may not reference a
          local type
      xxx<no_linkage>();
          ^
          ^

"ComeauTest.c", line 25: error: a template argument may not reference a
          non-external entity
          Hint: http://www.comeaucomputing.com/techtalk/templates/#stringliteral
    foo<&a>().bar();
        ^

"ComeauTest.c", line 27: error: a template argument may not reference a
          non-external entity
          Hint: http://www.comeaucomputing.com/techtalk/templates/#stringliteral
    foo<&c>().bar();
        ^

3 errors detected in the compilation of "ComeauTest.c".

时间: 2024-10-13 16:30:31

C++ 匿名namespace的作用以及它与static的区别的相关文章

C++ 匿名namespace的作用以及与static的区别

匿名namespace的作用以及它与static的区别 一.匿名namespace的作用 在C语言中,如果我们在多个tu(translation unit)中使用了同一个名字做 为函数名或者全局变量名,则在链接阶段就会发生重定义错误,为了解决这个 问题,我们可以在定义这些标识符(identifier)的时候加上static关键字修 饰以限制它只在一个tu范围内可见. C++继承了C语言中static关键字的这个用途,我们依旧可以使用static来避免 多个tu中使用同一个标识符带来的重定义问题.

(转载)OC学习篇之[email&#160;protected]关键字的作用以及#include和#import的区别

前一篇文章说到了OC中类的三大特性,今天我们来看一下在学习OC的过程中遇到的一些问题,该如何去解决,首先来看一下我们之前遗留的一个问题: 一.#import和#include的区别 当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误 但是使用两次#import的话,不会报错,所以他可以解决重复导入的问题,他会做一次判断,如果已经导入一次就不导入了 二.关键字@class的作用 在来看一下OC中的关键字@class的作用,在

OC学习篇之[email&#160;protected]关键字的作用以及#include和#import的区别

前一篇文章说到了OC中类的三大特性:http://blog.csdn.net/jiangwei0910410003/article/details/41707161今天我们来看一下在学习OC的过程中遇到的一些问题,该如何去解决,首先来看一下我们之前遗留的一个问题: 一.#import和#include的区别 当我们在代码中使用两次#include的时候会报错:因为#include相当于拷贝头文件中的声明内容,所以会报重复定义的错误 但是使用两次#import的话,不会报错,所以他可以解决重复导入

Ext中namespace的作用(转)

http://hintcnuie.iteye.com/blog/146813 Ext中在每一个页面中添加一个namespace呢,就像下面的代码: // create namespace Ext.namespace('myNameSpace'); // create application myNameSpace.app = function() { // do NOT access DOM from here; elements don't exist yet // private varia

C++ namespace的作用

namespace:命名空间或者叫名字空间,传统的c++只有一个全局的namespace,但是由于现在的程序规模越来越大,程序的分工越来越细,全局作用域就变得越来越拥挤,每个人都可能使用相同的名字来实现不同的库,于是程序员在合并程序的时候就可能出现名字的冲突.namespace引入了复杂性,解决了这个问题.namespace允许像类.对象.函数聚集在一个名字下,本质上讲namespace是对全局作用域的细分. 比如这样的程序大家经常见到的: 1 #include <iostream> 2 us

Javascript匿名函数的作用之一:创建临时的命名空间,防止全局变量污染

如果你写了一段js模块的代码,这段代码将用在不同的程序中(对客户端Javascript来说通常是在各种各样的网页中).假定这段代码定义了一个用以存储中间计算结果的变量,问题来了,这个变量可能会同其他代码中的全局变量发生冲突.解决的办法是将模块代码放入一个函数内部,然后立即调用这个函数.这样全局变量就变成了函数内部的局部变量: function() myModule(){ //模块代码,这个模块所用的所有变量都是局部变量,而不会污染全局命名空间 } myModule(); //立即调用这个函数 这

namespace的作用及用法

namespace 所谓namespace,是指标识符的可见范围.C++标准库中的所有标识符都被定义在一个名为 std 的namespace 中. 一.<iostream>和<iostream.h>格式不一样,前者没有后缀.实际上,在你自己编译器的include路径下,你可以看到这两个是两个不同的文件,而且打开后里面的代码也不同.后缀为 .h 的头文件C++标准以明确提出不支持了,C++标准为了与C区分开,也为了能够正确的使用命名空间,规定了头文件不适用后缀 .h .因而 1.当使

c++ anonymous namespace -- 匿名空间

匿名空间,匿名类,匿名联合体,匿名结构体. 匿名空间 #include <stdio.h> namespace A { int ID = 1; } namespace { int ID = 11; } namespace B { int ID = 21; } int main(void){ printf("ID %d \n",ID); } 输出 ID 11 这里用到是C的函数,减少std空间的干扰.这里输出的是匿名空间的内容. 那么就类似于如下用法 namespace __

简单介绍Javascript匿名函数和面向对象编程

忙里偷闲,简单介绍一下Javascript中匿名函数和闭包函数以及面向对象编程.首先简单介绍一下Javascript中的密名函数. 在Javascript中函数有以下3中定义方式: 1.最常用的定义方式: function functionVal(variable){ return 3*variable; } 2.使用Function构造函数,将函数的参数和函数体内容作为字符串参数[不建议使用]: var objFunction=new Function('variable','return 3