ブログ 約3分

C++プロジェクトで名前空間を使う5つの一般的な理由

Share this article
C++プロジェクトで名前空間を使う5つの一般的な理由

名前空間は1995年にC++標準へ導入され、通常は次のように定義されます。

名前空間は新しいスコープを定義し、名前衝突を回避する方法を提供します。

C++で名前空間が最もよく使われる目的は、名前衝突の回避です。最近のC++コードでは名前空間が広く使われていますが、古いコードの多くではこの機能が使われていません。 

多くのC++プロジェクトのソースコードを調べた結果、これらのプロジェクトで名前空間が使われる一般的な理由がいくつか見つかりました。

1. 名前衝突を回避する

前述のとおり、これが最も一般的な理由です。この場合、名前空間の使用は主にコンパイラに利益をもたらすものであり、開発者にとってコードの可読性や保守性が必ずしも向上するわけではありません。

2. アプリケーションをモジュール化する

モダンなC++ライブラリは、コードベースをモジュール化するために名前空間を広範に使い、「namespace-by-feature」アプローチを採用しています。namespace-by-feature では、機能セットを反映するように名前空間を使います。単一の機能に関連するすべての項目(その機能に関連する項目だけ)を1つの名前空間に配置します。その結果、凝集度とモジュール性が高く、名前空間間の結合が最小限の名前空間になります。密接に連携する項目は、互いに近い場所に配置されます。

Boostは機能別にグループ化する最良の例です。数千の名前空間が含まれており、それぞれが特定の機能をグループ化するために使われています。

3. 匿名名前空間

名前のない名前空間は、グローバル static 変数の代替手段を提供します。匿名名前空間には、それが宣言されたファイル内からのみアクセスできます。

4. 列挙型の問題に対する回避策

C++の「従来の」列挙型は、列挙子を周囲のスコープへエクスポートします。そのため、同じスコープ内の異なる2つの列挙型が同名の列挙子を定義すると、名前衝突が発生する可能性があります。

大規模プロジェクトでは、2つの異なる列挙型が同じ列挙子名を使わないという保証はありません。この問題はC++11で enum classを使うことで解決されました。enum class は、列挙値を列挙型名のスコープ内に暗黙的に閉じ込めます。

C++11以前は、列挙型を名前空間内に宣言するのが一般的な回避策でした。たとえば、次のように列挙型を宣言する代わりに、

enum status{
  status_ok,
  status_error
};

名前空間内で宣言できました。

namespace status{
       enum status{
         ok,
          error
  };
}

多くのC++プロジェクトがこの手法を使っています。たとえば、Unreal Engineのソースコードではこのテクニックが広く使われています。

5. 慣例による詳細の隠蔽

コードがヘッダーファイルに実装されるテンプレートライブラリでは、特定の型が実装の詳細であり、直接使用すべきではないことをライブラリ利用者へ示すと便利です。C#では「internal」キーワードがこの目的を果たしますが、C++には公開型をライブラリ利用者から隠す同等の方法がありません。

Boostライブラリの開発者たちが先駆けたモダンC++の一般的なイディオムは、モジュールの実装の一部でありながら(つまり公開APIの一部ではないものの)公開する必要があるシンボルを、慣例的に detail と名付けられた独立したサブ名前空間へ分離することです。

たとえば、Boost.Mathのドキュメントには次のように記載されています。

アプリケーションから使用することを意図していない関数は boost::math::detail
Share this article