c++の名前空間

c++名前空間,よくわかっていなかったのでちょっと実験してみた.c++名前空間は,Javaのパッケージのような概念で,クラス名やグローバル変数などの,グローバルなスコープを持つ名前空間を分割するために用いられる.こういう仕掛けがないと,ライブラリと関数名がぶつかったりして,大規模なプログラムが構成しにくくなる.

Javaのパッケージは,ファイルとそのファイルの置かれるパスに依存しており,一つのファイルの中に複数のパッケージに属するエンティティを定義することはできない.これに対してC++名前空間はプログラム中で宣言するだけなので,一つのファイルのなかにいくらでも名前空間を定義していくことができる.

名前空間のなかで定義されたエンティティを参照するには'::'を用いる.

namespace foo {
  int a = 1;
}
namespace bar {
  int a = 2;
}

printf("%d\n", a);       // compile error 名前解決できない
printf("%d\n", foo::a);  // 1
printf("%d\n", bar::a);  // 2

または using namespace で利用する名前空間を指定する.

using namespace foo;
printf("%d\n", a);       // 1 

using namespace のスコープは変数などと同じで,明示的に終了することができない.次のようにするとコンパイルエラーが生じる.

using namespace foo;
printf("%d\n", a);  
using namespace bar;
printf("%d\n", a);       // compile error

a をfoo::a だと解釈するべきなのか,foo:bだと解釈するべきなのかが曖昧になるからである.面白いのはこの行がなければcompile errorは生じないこと.つまり複数のusingによって潜在的な衝突が起こっていても,顕在化させるような参照が無い限りエラーが出ない.

using namespaceのスコープはブロックで制御することができる.

{
  using namespace foo;
  printf("%d\n", a);       
}
{
  using namespace bar;
  printf("%d\n", a);       // 今度は大丈夫
}

ネスト

名前空間はネストすることが可能.Java名前空間は一見階層構造を持っていてネストしていそうな感じだが,実はフラットな構造になっていて,見かけ上の階層構造を利用することはできない.例えば,java.rmi.* をインポートしていても,java.rmi.registry.Registory を java.rmiを省略して参照することはできない.

これに対してC++名前空間の階層化はちゃんとしている.

namespace foo {
  namespace bar {
    int a = 1;
  }
}

printf("%d\n", foo::bar::a); // 1
{
  using namespace foo;
  printf("%d\n", bar::a);    // 1
  {
    using namespace bar;
    printf("%d\n",    a);    // 1
  }
}

名前空間とクラス名

名前空間とクラス名は同じであってもかまわない.

namespace foo {
  ...
};
class foo {
  ...
};

同じ名前であっても参照され方で区別できるので問題ないようだ.

名前空間とstatic 変数

ここでちょっと疑問になるのが,static変数の参照も'::'で行うということ.同じ名前のクラス名と名前空間があり,それぞれ同じ名前が中に定義されていた場合に何が起こるのだろう?

namespace foo {
  int a = 0;               // foo::a
}
class foo {
 public:
  static const int a = 1;  // foo::a
};

printf("%d\n", foo::a);

まあコンパイルエラーで落ちるだろうと思っていたら,少なくとも手元のg++4.3では落ちない.実行してみると

1

となる.つまりクラスのインスタンス変数であると解釈されたということらしい.クラスfooが定義されていると名前空間 fooの値には::ではアクセスできない.using をつかえば大丈夫.

namespace foo {
  int a = 0;
  int b = 0;
}
class foo {
public:
  static const int a = 1;
};

printf("%d\n", foo::a);
printf("%d\n", foo::b);  //error: ‘b’ is not a member of ‘foo’
{
  using namespace foo;
  printf("%d\n", b);     // OK
}