C++編【言語解説】 第3章 名前空間

この章の概要

この章の概要です。

名前空間

名前空間は、プログラム内で使われる名前(変数名、関数名など)をグループ分けする仕組みです。 この仕組みによって、同一の名前であっても、グループの名前によって両者の区別を付けることができるため、 名前の衝突を避けられます。

例えば、int型の配列の要素をコピーする関数を copy と名付け、生徒の得点データをコピーする関数も copy と名付けてしまうと、 名前が衝突してしまいます。 こういうとき、C言語であれば、前者を int_array_copy、後者を student_copy のようにして、 プリフィックスを付けるようにして区別を付けます。

C++ の名前空間の仕組みも、考え方はほとんど同じです。 名前空間を使う場合、次のように書きます。

namespace int_array {
	void copy(int* dest, const int* src);
}

namespace student {
	void copy(Data* dest, const Data* src);
}

namespaceキーワードに続けて、名前空間の名前を書きます。 更に { } で範囲を指定すると、{ } の内側の定義が名前空間に含まれるようになります。

この状態で、それぞれの関数を呼び出す際には、

int_array::copy(dest, src);
student::copy(dest, src);

というように、「名前空間名::名前」という構文を使います。 ここで、:: は、スコープ解決演算子と呼ばれており、名前空間だけに使われる記号でないことを断っておきます。 他の場面での使われ方は、第15章で説明します。

ここまでの例では、関数を名前空間に入れていますが、変数でも構造体や列挙型などでも同様です。

namespace student {
	
	enum Sex {
		Man,
		Woman,
	};

	struct Data {
		char name[128];
		Sex  sex;
		int  score;
	};

	void copy(Data* dest, const Data* src);
}

この例でいえば、生徒を管理する情報のすべてが student名前空間に収められています。 このように、何らかの意味のある集まりで、1つの名前空間を形成するのが普通です。

このコードを観察してみると分かるように、同じ名前空間の中では、名前空間名::名前の形でなくてもアクセスできます。 例えば、student::copy関数の仮引数に使われている Data型は、正確に表現すると student::Data ですが、 この関数宣言そのものが student名前空間の中にあるので、student:: は省略できます。 つまり、同一の名前空間内にいるのなら、わざわざ名前空間名を指定する必要はありません(しても構いません)。


なお、唯一、名前空間に入れることができないのが、プリプロセッサの定義です。 例えば、

namespace student {
	
	#define SCORE_MAX (100)
	
}

このような定義のとき、student::SCORE_MAX のような形でアクセスすることはできません。 名前空間内に定数が必要なら、const変数を使います。

namespace student {
	
	const int SCORE_MAX = 100;
	
}

これなら、student::SCORE_MAX という形でアクセスできます。

名前空間の分割

名前空間を複数の場所で定義しても、それらは同一とみなされます。

これができないと、ソースファイルとヘッダファイルをうまく書けません。

// student.cpp

#include "student.h"

namespace student {
	void copy(Data* dest, const Data* src)
	{
	}
}
// student.h

namespace student {

	enum Sex {
		Man,
		Woman,
	};

	struct Data {
		char name[128];
		Sex  sex;
		int  score;
	};

	void copy(Data* dest, const Data* src);
}

これまでの章で、std::cout のような記述が登場していますが、この std も名前空間です。 C++標準ライブラリはすべて std名前空間に含まれていますが、幾つものヘッダが提供されていることから分かるように、 それぞれの定義は、別個のファイルに記述されています。 これができるのも、同一の名前空間を別個のファイルに記述できるからです。

名前空間のネスト

名前空間はネストさせることができます。

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

この場合、func() を呼び出すには、名前空間の外側からは、A::B::func() という形になります。
冒頭で見たように、同一の名前空間内からは、名前空間名を指定しなくても良いので、 func2() の中から func() を呼び出す際には、単に func() と書けます。

各階層に関数があった場合はどうでしょう。 例えば、次のような場合を考えます。

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

この場合、func3 の完全な名前は「A::func3」です。 つまり、A という名前空間内にあるので、ここから func() を呼び出す際には、B::func() と書くことができます。
逆に、func() から func3() を呼び出す際には、A::func3() と書きます。 自分の親になっている名前空間を取得する手段がないので、完全な名前である「A::func3」と書いている訳です。


名前空間のネストは、自分用のライブラリを作る場合に活用できます。 例えば、MyLib という名前空間を定義し、すべての関数や列挙型などの定義をその中に収めます。 更に、MyLib の中を分割し、数学的な処理を Math名前空間に入れたり、汎用的なユーティリティ関数を Util名前空間に収めたりするのです。

// Math.h
namespace MyLib {
	namespace Math {
	}
}
// Util.h
namespace MyLib {
	namespace Util {
	}
}

こうすれば、他のライブラリ作者が提供する Math名前空間や Util名前空間と被る可能性を減らせます。

無名名前空間

名前のない名前空間というものがあります。 これを無名名前空間と呼び、次のように記述します。

namespace {
}

namespaceキーワードの直後を省略するだけで、あとは通常の名前空間と同じです。

無名名前空間は、それが書かれたソースファイルからだけアクセスできる場所を提供します。 これは、C言語における、静的グローバル変数(C言語編第23章参照)と同じような意味合いを持っています。 C++ においては、静的グローバル変数を使うよりも、無名名前空間を使うことが推奨されています

無名名前空間の内部にある変数や関数などを参照する際には、スコープ解決演算子を必要としません。 例えば、次のようになります。

namespace {
	const int FILE_PATH_MAX = 260;
}

char path[FILE_PATH_MAX];  // ::演算子は必要ない

ここで、スコープ解決演算子を使って、

char path[::FILE_PATH_MAX];

このように書くと、意味が変わってしまうことに注意して下さい。 スコープ解決演算子の :: の手前を省略したこの書き方は、グローバル名前空間へのアクセスを意味しています。 グローバル名前空間については、次の項で説明します

グローバル名前空間

グローバル名前空間は、namespace のブロックに囲まれていない場所を表します。

前述したように、スコープ解決演算子で :: の手前を省略すれば、グローバル名前空間をアクセスできます。 また、グローバル名前空間内からアクセスする際には、これまでのルールと同様に、スコープ解決演算子を使わずにアクセスできます。

なお、プログラムの開始位置である main関数については、必ずグローバル名前空間に置かなければなりません。

usingディレクティブ(using指令)

名前空間を使うことによって、名前の衝突を避けることができるものの、毎回、「xxxx::」のような修飾を加えるのも面倒です。 そこで、特定の名前空間について、この記述を省略する構文が存在します。 これを usingディレクティブ(using指令)と呼びます。

前章で登場したプログラムで試してみます。 元々は、次のようなコードでした。

#include <iostream>
#include <cstring>

int main()
{
	const char* str = "abcde";
	std::cout << str << std::endl;

	std::size_t len = std::strlen(str);
	std::cout << len << std::endl;
}

「std::」が頻繁に登場しています。 ここに、usingディレクティブを加えると、次のように書けます。

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
	const char* str = "abcde";
	cout << str << endl;

	size_t len = strlen(str);
	cout << len << endl;
}

usingディレクティブは、「using namespace 名前空間名;」という構文になっています。 この一文以降、指定した名前空間は、明示的に指定しなくてもアクセスできるようになります。 効力が消えるのは、この一文を記述したブロックの末尾に達したところです。

これは便利であると同時に、混乱を招く可能性もあることを意識しておく必要があります。 例えば、

namespace Util {
	int func();
}

int func();

int main()
{
	using namespace Util;
	
	func();
}

このようなプログラムで、main関数内で呼び出している func() は、Util::func() と ::func() のどちらになるでしょうか。 このプログラムでは、曖昧であるとみなされてコンパイルエラーになります。
このプログラムから曖昧さを無くすには、func() の呼び出し部分を、

Util::func();

か、

::func();

のように明示的に書かなくてはなりません。 これは結局、using namespace Util; の意味を失わせます。
明示的に名前空間名を指定することによって、ソースコードの可読性が大きく損なわれるのでなければ、 基本的には usingディレクティブを使わずに、明示的に書いた方が良いです。

また、このような混乱を回避するためにも、usingディレクティブをヘッダファイル側に記述しないようにして下さい。 ヘッダファイルは、様々な場所からインクルードされるため、usingディレクティブの効力を、 他のソースファイルやヘッダファイルにまで撒き散らしてしまいます。 結果的に、何が省略されているのか把握しづらくなり、混乱の元になります。
従って、ヘッダファイル内では、面倒でも、常にスコープ解決演算子を使って明示的に名前空間名を指定すべきです。

using宣言

using宣言は、ある名前を、現在のスコープ内に取り込む機能です。 再び、先ほどのプログラムを例に挙げます。

#include <iostream>
#include <cstring>

int main()
{
	const char* str = "abcde";
	std::cout << str << std::endl;

	std::size_t len = std::strlen(str);
	std::cout << len << std::endl;
}

ここで、std::cout と std::endl は複数回登場しており、これらだけでも簡潔に記述したいとします。 こういう場合に、using宣言が使えます。

#include <iostream>
#include <cstring>

int main()
{
	using std::cout;
	using std::endl;

	const char* str = "abcde";
	cout << str << endl;

	std::size_t len = std::strlen(str);
	cout << len << endl;
}

using宣言は、「using 名前空間名::名前;」という構文になっています。 using宣言の意味合いとしては、指定した名前を現在のスコープに持ち込むということになります。 効力が消えるのは、この一文を記述したブロックの末尾に達したところです。
例えば、

#include <iostream>

namespace Util {
	int value = 50;
}

int main()
{
	using Util::value;  // Util::value が value という名前でスコープ内に追加される
	
	int value = 10;
	
	std::cout << value << std::endl;
}

この場合、main() で宣言されている value の他に、Util::value が value という名前で main() のスコープ内に追加されることになります。 そのため、main() の value と、Util::value とが重複宣言とみなされてコンパイルエラーになります。

一方で、usingディレクティブの場合、

#include <iostream>

namespace Util {
	int value = 50;
}

int main()
{
	using namespace Util;  // Util:: を省略できるようになる
	
	int value = 10;
	
	std::cout << value << std::endl;
}

コンパイルエラーにはならず、10 が出力されます。 usingディレクティブは、名前をスコープに追加するという効果ではなく、 単に「Util::」と書くべき場面で「Util::」を省略できるようにするだけです。
value という名前だけでのアクセスは、C言語のルールと同様に、よりローカルに近いところから名前を探し、 main() の中で宣言されているローカル変数の value を認識します。 したがって、何も曖昧さはないので、コンパイルエラーとはならないのです。


usingディレクティブよりは、using宣言を使う方が、影響範囲が狭いので望ましいと言えます。 ですが、明示的な指定でも問題がないようならば、using宣言することを避けた方が良いです。
もちろん、using宣言も、usingディレクティブ同様、ヘッダファイル内での使用は避けた方が良いです。

エイリアス

名前空間には別名(エイリアス)を付けることができます。 この機能によって、長い名前の名前空間を短くしたり、ネストした名前空間の完全名を短くしたりできます。

#include <iostream>

namespace MyLib {
	namespace Util {
		void func();
		
		void func()
		{
			std::cout << "OK" << std::endl;
		}
	}
}

int main()
{
	namespace A = MyLib::Util;

	MyLib::Util::func();
	A::func();
}

名前空間のエイリアスは、「namespace 別名 = 元の名前;」という構文で作ることができます。 上のサンプルプログラムの場合、MyLib::Util名前空間に、A という別名を付けています。

この機能は、例えば、あるライブラリを提供する側が、最初に公開したバージョン1の機能に対して、 新たにバージョン2を付け加えたいという場合に利用できます。

// ライブラリ側のコード
namespace XXLib {
	namespace V1 {
		// バージョン1の機能
	}
}

// ライブラリ使用者側のコード
namespace Library = XXLib::V1;  // XXLib のバージョン1を使う

Library::func();  // バージョン1の func() を呼び出す

この例では、XXLib という他者が提供したライブラリのうち、バージョン1の部分を使おうとしています。 毎回、XXLib::V1::func() のように書くのではなく、バージョン番号を表す名前空間名に Library という別名を付けて、 Library::func() と書くようにしています。
この後、XXLib の提供者が、バージョン2を公開します。

// ライブラリ側のコード
namespace XXLib {
	namespace V1 {
		// バージョン1の機能
	}
	namespace V2 {
		// バージョン2の機能
	}
}

// ライブラリ使用者側のコード
namespace Library = XXLib::V2;  // XXLib のバージョン2を使う

Library::func();  // バージョン2の func() を呼び出す

ライブラリ側は、V1 の機能を残したままで、V2 の機能を追加します。 使用者側は、エイリアスを定義している箇所を修正し、Library という名前が XXLib::V2 の方を意味するように書き換えます。 これで、V2 への移行が(ライブラリ側の重大な仕様変更がなければ)完了するという訳です。


この機能もまた、便利であると同時に混乱を招く可能性をはらんでいます。 可能であれば使用を避け、ヘッダファイルでの使用は禁止とすべきでしょう。

C++11 (インライン名前空間)

C++11

C++11 では、インライン名前空間という機能が追加されました。 これは、名前空間名による修飾を省略しても、その名前空間内へアクセスできるというものです。

#include <iostream>

namespace MyLib {
	inline namespace Util {
		void func();
		
		void func()
		{
			std::cout << "OK" << std::endl;
		}
	}
}

int main()
{
	MyLib::Util::func();
	MyLib::func();
}

実行結果:

OK
OK

名前空間を作る際に、inlineキーワードを付けて、「inline namespace 名前空間名」とすれば、 インライン名前空間になります。
このサンプルプログラムでは、Util名前空間がインライン名前空間になっています。 ですから、「MyLib::func();」のように呼び出すことが可能になります。

この機能は、VisualC++ 2013 では対応しておらず、2015/2017 は対応しています。 clang 3.7 は対応しています。


練習問題

問題@ 次のプログラムの出力結果を答えて下さい。

#include <iostream>

void func();

void func()
{
	std::cout << "::func()" << std::endl;
}

namespace Util {
	void func();

	void func()
	{
		std::cout << "Util::func()" << std::endl;
	}
}

int main()
{
	using Util::func;

	func();
}

問題A 問題@のプログラムで、main関数の内側にある using宣言を、

using namespace Util;

に変更した場合、実行結果はどうなりますか?


解答ページはこちら

参考リンク

更新履歴

'2017/3/25 VisualC++ 2017 に対応。

'2016/10/15 clang の対応バージョンを 3.7 に更新。

'2015/10/12 clang の対応バージョンを 3.4 に更新。

'2015/9/5 VisualC++ 2012 の対応終了。

'2015/8/18 VisualC++2010 の対応終了。

'2015/8/15 VisualC++ 2015 に対応。

'2014/10/18 clang 3.2 に対応。

'2014/2/1 VisualC++ 2013 に対応。

'2014/1/20 「C++11 (インライン名前空間)」の項を追加。

'2013/3/28 新規作成。



前の章へ

次の章へ

C++編のトップページへ

Programming Place Plus のトップページへ