不過因為今天寫 C++ 程式的時候,碰到這樣的 warning:

warning: 'Foo' has a field 'Foo::impl_' whose type uses the anonymous namespace.

原來是因為,我在 foo.hpp 這個 header 檔裡,寫了一個 anonymous namespace[1],如下:

#ifndef FOO_HPP_INCLUDED
#define FOO_HPP_INCLUDED

namespace { // anonymous, so only library implementer know how to use it
    class FooImpl { ... };
}

class Foo
{
public:
    ...
private:
    FooImpl impl_;
};

#endif /* FOO_HPP_INCLUDED */

因為還沒有引入如 boost::shard_ptr<> 的機制,考量到 exception/thread safety,不想要讓 impl_ 是個指標[2],因此利用 anonymous namespace 的方式,把 FooImpl「藏」起來。不過,這樣的寫法,有潛在的問題,因此 gcc 發出 warning 警告。

Compiler 在編譯時 anonymous namespace 時,相當於以 compilation unit 為單位,為裡面的 anonymous namespace,取一個獨一無二的名字,然後自動 using 之。若 header 檔被不同的 source 檔引入,成為不同的 compilation unit,則 anonymous namespace 所自動產生的名字,皆不相同,共同運作時,就會產生問題。

以上例來說,foo.hpp 可能被 a.cppb.cpp 分別引入,兩者分別建構了 Foo 類別的物件,依據以上說明,a.cpp 裡面的 Foo 類別的物件,其 impl_ 成員的型別,可能是 anonymous_a;而 b.cpp 裡面的 Foo 類別的物件,其 impl_ 成員的型別,則可能是 anonymous_b。同樣是 Foo 類別的物件,在不同的 compilation unit 裡,其 impl_ 成員,型別卻不同,這個歧異,肯定會造成問題。

因此,我們應該避免在 header 檔,使用 anonymous namespace。

可是,若不能在 header 裡使用 anonymous namespace 的話,那 anonymous namespace 到底有甚麼存在的意義呢?畢竟,我們大可以直接在 source 檔裡宣告類別,反正其他 source 也看不到,不就有了 anonymous namespace 的效果了嗎?

其實,anonymous namespace 還是有其存在意義的,畢竟,anonymous namespace 依然是一個 namespace,而 namespace 的作用,就在於避免 name collision。好比說,我們永遠無法保證,source 檔所引入的其他程式庫的 header 檔,在未來版本裡,會不會冒出一個,名字跟我們想要藏起來自己用的類別、函式或變數。而有了 anonymous namespace,我們就可以保證,此時不會發生 name collision。

參考資料:


  1. 這裡有更詳細的說明:《Namespaces and anonymous namespaces》。
  2. 如果是個指標,就得負起管理其生死的責任,頗為麻煩。雖然說也可以用 std::auto_ptr<>,然後以 impl_.get() 取用以避免所有權轉移,也是可以達到偷懶的效果,不過總之我就是選了 anonymous namespace 這個辦法。
  3. 好久沒看 comp.lang.c++.moderated 了,從學校畢業後,就與 newsgroup 絕緣,一來沒空,二來沒權限。