無名namespace




グローバルに宣言された、

list-1
    static int i;


は、内部リンケージがある。C++規格ではこのような大域staticは将来的になくなる。(ARM §3.3.1.5)

よって、これらは代わりに無名のnamespaceに閉じ込めてやる必要がある。

namespaceについて知っておくべきことは、以下のこと。

・namespaceは型ではない。
・namespaceはアクセス制御できない。(すべてpublic扱い)
・namespaceはオープンである。つまり、1つの名前空間が1つの翻訳単位(=ソースファイル単位)でテキストとして連続している必要がなく、複数の翻訳単位にまたがることもできる。
・namespaceは、グローバルスコープか、別の名前空間内で宣言しなければならない。クラスや関数内部では宣言できない。

list-2
namespace n
   {
   int a;
   } // このあと`;`は不要

namespace n // 同じ名前のnamespaceをつづけて書くことも可能
   {
   int b;
   }

int i = n::a; // aはnamespace nに存在して、かつpublic扱い

n x; // namesapceのnは型ではないのでエラー

class c
   {
   namespace n // クラス内でnamespaceの宣言は不可なのでエラー
      {
      }
   };


・using-directive(指令)とusing-declarationの(宣言)違い(ARM §3.3.1.2)

C++標準ライブラリはほとんどがnamespace std内に閉じ込められている。名前にいちいちstd::をつけるのはたいへん面倒なので、

list-3
    using namespace std;

として、名前をスコープの範囲内に置くことができる。特定の名前だけを選択したいときは、

list-4
    using std::string;

のようにする。

前者はusing-directiveと呼ばれる。後者は、using-declarationと呼ばれる。

using-directiveは名前空間名による限定なしでアクセスを可能にする。
一方、using-declaration は、それを宣言したスコープに名前を追加する。

よって、以下のようになる。

list-5
  namespace N {
    int x,y;
  }

  void func() {
    float x;
    using N::x;          // エラー (x の2重宣言)
    using namespace N;
    x = 1.0;             // ローカルの x にアクセス
    N::x = 1;            // N::x にアクセス
    y = 2;               // N::y にアクセス
  }  


・namespaceのメンバをヘッダで宣言して実装ファイルで定義する

list-6 doodle.h
namespace doodle
   {
   void f();
   }


list-7 doodle.cpp
#include "doodle.h"
void doodle::f()
   {
   // ...
   }


・無名namespace

namespaceで空間の名前を指定しなかった場合、翻訳単位内にある無名の名前空間すべてについて同じ“隠し名”を使用される。各翻訳単位において他と重複しない隠し名がそれぞれ割り当てられる。すなわち、

list-8
namespace {
   void f() {}
}

無名namespaceで閉じ込められたfは、外部リンケージを持つにもかかわらず、その名前空間が翻訳単位で異なる隠し名であるために外部からアクセスされることは決してない。すなわち、内部リンケージを持つかのごとく使うことが出来る。

・無名namespace≠内部リンケージ

list-9
void f() {}
static void f(int){}
static void g()
   {
   f(0); // OK, calls `f(int)`
   f();  // OK, calls `f()`
   }


これを無名namespaceで囲って以下のように変形した場合、

list-10
void f(){}
namespace{ void f(int){}}
namespace{
   void g(){
      f(0); // `f(int)`を呼び出せる
      f();  // これは`f(int)`を呼び出そうとしてエラーになる
      }
   }


後者はエラーになる。これは、無名namespaceによって名前fを隠匿させられたためで、後者は以下のように書く必要がある。

list-11
       ::f();  // これなら`f()`を呼び出せる



参考:
Deep C++ :
http://www.microsoft.com/japan/msdn/library/ja/jpdndeepc/htm/deep04062000.asp
C++ Labyrinth :
http://www.fides.dti.ne.jp/~oka-t/cpplab-tips-1.html