1、匿名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相同。
2、匿名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三種狀況
-------------------------------------------------------------------------------
express
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".
ide