名前空間

Home > 2006-06 / CからC++へ > This Entry [com : 0][Tb : 0]

2006-06-13

Cでは、プログラムがある程度以上の規模になってくると、外部識別子の衝突を防ぐために、モジュールごとに何らかの接頭辞を付けるなどの工夫が必要でした。しかし、その結果として、どうしても識別子が長く汚くなるという弊害があったと思います。

C++では、そのような問題を解消するために、「名前空間」という機能が設けられています。実はCの仕様にも名前空間というものがあったのですが、これは全く別の概念で、タグ名やラベル名やフィールド名が、一般の識別子とは異なる名前空間にあるため、同じ有効範囲にあっても同じ名前を使えるといったものでした。例えばこんな感じです。

void func(void)
{
  struct foo
  {
    int foo;
  } foo;
  goto foo;
foo:
}

良し悪しは別にして、同じ有効範囲内で、同じ名前 foo を、こんなに使い回すことができるのは、Cの名前空間によるものでした。

C++で導入された名前空間は、これとは全く異なり、キーワードnamespaceを使って明示的に定義します。例えば、

namespace A
{
  void foo();
}
namespace B
{
  void foo();
}
void foo();

のように記述した場合、3つのfoo関数は別の関数ということになります。そして、Aという名前空間で定義されたfooを参照するにはA::fooと記述します。Bという名前空間で記述されたfooを参照するにはB::fooと記述します。

どのnamespaceの囲みにも属していない最後のfooも、実は大域的(グローバル)名前空間という一種の名前空間に属しているとみなされます。明示的に大域的名前空間のfooをあることを示すには、::fooのように記述します。これらの記述の際に使用した :: というのは、有効範囲解決演算子という一種の演算子で、左オペランドには、名前空間かクラス名を指定することができます。

:: が名前空間を解決する演算子であることからもわかるように、同じ名前空間から識別子を参照する場合には、一々名前空間を指定する必要はありません(指定してもかまいません)。つまり、同じモジュール内であれば、冗長な名前を使う必要はなく、外部から参照する場合だけ、フルネームで参照すればよいことになります。

この有効範囲解決演算子を使うと、次のようなことも可能になります。

int foo;

void func()
{
  int foo;
  foo = ::foo;
}

このように、より内側の有効範囲で同名の識別子(ここではfoo)を宣言した場合でも、外側である大域的名前空間のfooにアクセスすることができています。もっとも、このような使い方は混乱の原因ですので、あまり積極的には使わない方がよいでしょう。

ところで、名前空間は入れ子にすることもできます。

namespace A
{
  namespace B
  {
    void func();
  }
}

のように、何重かに入れ子にすることで、サブモジュール化をするのに便利になっています。しかし、あまり入れ子のレベルが深くなると、毎回、有効範囲解決演算子を用いて名前空間を指定するのが面倒です。そんな場合にはキーワード using を使用します。

using namespace A::B;

とすれば、A::B名前空間に属している識別子を、名前空間の指定なしで参照することができるようになります。ただし、その結果として、名前の解決が曖昧になる場合もありますので、十分注意が必要です。また、

using A::B::func;

のようにすれば、A::B名前空間に属しているfuncという識別子だけを、名前空間の指定なしで使用することができるようになります。これは識別子単位で指定することになるので、関数が多重定義されている場合には、それらの全てに対して働くことになります。

さらに、名前空間の名前が非常に長いような場合には、別名を付けることも可能です。別名を付けるには、

namespace C = A::B;

のようにします。

C++の入門書などでは、問答無用で using namespace std; などと大域的名前空間有効範囲で行っているものを多く見かけますが、これはお勧めできません。最低限、関数の中で行うなど、影響範囲を最小限に抑える努力をしないと、思わぬトラブルに遭遇する原因になります。

ところで、名前のない名前空間というのも存在します。無名名前空間というのですが、次のように記述します。

namespace
{
  void func();
}

無名名前空間は「無名」なので、有効範囲解決演算子で名前空間を指定することはできません。というより指定する必要がありません。つまり、上の場合、無名名前空間の外部であっても、funcとするだけで、参照することができます。

無名名前空間というのは、暗黙のうちにコンパイラが名前を振ることになっています。同じ翻訳単位に書かれた無名名前空間には同じ名前がつけられるのですが、異なる翻訳単位の無名名前空間には別の名前が付けられます。結果として、無名名前空間に属している識別子は、他の翻訳単位から参照することができなくなります。

Cでは、他の翻訳単位から参照できなくするためには、staticを付けて内部結合にしていました。C++でも(今のところ)同じ方法は使えますが、これは廃規格の候補ともなっています。他の翻訳単位から参照されないようにするには、できるかぎり無名名前空間を使うことをお勧めします。

Comment

Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/76-f6e7c60d

C++と組み込み環境 | Page Top▲

New >>
constオブジェクト
<< old
例外処理機構
ブログ内検索
RSSフィード