移植性の高いプログラムの書き方
「using指令を使うときの注意点」編

しのはらのC++実験室トップページ
「移植性の高いプログラムの書き方」トップページ

標準ライブラリを使う際の最新の作法

 最新のC++仕様である標準C++では、入出力クラスライブラリ(I/Oストリームライブラリ)を次のように使用します。

#include <iostream>

int main()
{
	std::cout << "Hello World" << std::endl;
}
このように、coutendlなどの名前の前にstd::をつけて書きます。これが基本です。ただし、いつもstd::をつけるのは面倒なので、次のように省略する方法があります。
#include <iostream>
using namespace std;

int main()
{
	cout << "Hello World" << endl;
}
いづれにしても、I/Oストリームライブラリがstd名前空間で宣言されているという事実に変わりありません。それに対し、古いC++の仕様では次のように書くのが唯一の正しい方法だったため、今でも互換性のためにこのような書き方が許されています。
#include <iostream.h>

int main()
{
	cout << "Hello World" << endl;
}
このプログラムのinclude指定で、<iostream>というヘッダ名ではなく<iostream.h>というファイル名を使用していることがポイントです。この場合、I/Oストリームライブラリがstd名前空間の中ではなくグローバル名前空間で宣言されるので、修飾子なしでcoutなどを使用できるわけです。

 しかしこれらの「省略方法」や「古い記述方」には気をつけるべきいろいろな問題があります。問題があるからこそ最新の記述方法が推奨されているのですから。

最新の作法に従うことの重要性

 標準C++ではstd名前空間の中に非常にたくさんのクラスや関数を宣言しています。そのため、プログラマが知らないうちにそれらと同じ名前のクラスや関数を作ってしまう可能性は大いにあります。そこで、もしそのような名前の一致が生じても問題が起きないようにするために、std名前空間という仕組みが有効に働きます。前述の最初のプログラム例に示すように、std名前空間内の識別子に対して常にstd::という修飾子を使用している限り、coutなどの名前のクラスや関数、変数などを新規に定義し使用することが確実に行えます。この「確実に」ということは重要です。運悪く名前の一致が起きたときに、コンパイルエラーになるようなプログラムはうまくありません。というのは、将来、標準C++ライブラリに新しいクラスや関数が加わったときに、それまで動いていたプログラムがエラーになる可能性があるようでは、困るということです。

 そういうわけで、using namespace std;#include <iostream.h>などの使用には危険が付きまとうのです。このことはあまり認識されていないようですが、将来にわたって使用可能なライブラリを書くプログラマにとっては、無視できない問題です。

 例を見てみましょう。これはmatrixというクラスを定義するMyMatrixというライブラリのヘッダファイルです。

// 危険なコード
#include <iostream>
// ↓礼儀正しく、独自の名前空間MyMatrix内ですべての宣言をしている。
namespace MyMatrix{
	class matrix{
		/* ... */
	};
	using namespace std;
	ostream& operator << (ostream& , const matrix& );
}
これは例えば次のように使用でき、正しく動作します。
int main()
{
	MyMatrix::matrix m;
	/* ... */
}
さて、このように礼儀正しく名前空間を使用した、何の問題もなさそうなプログラムですが、実は問題があります。もし将来、標準C++ライブラリにmatrixという名前のクラスが導入されたらどうなるでしょうか。当然新しいmatrixクラスはstd::matrixと表記され、MyMatrix::matrixとは名前空間が違うから、何の問題も起きない? いいえ違います。上のプログラムに色つきで示してあるusing namespace std;という文があるために、識別子の衝突エラーが発生してしまいます。もはやMyMatrix::matrixは2つのmatrixを指すために曖昧と判断されます。これはエラーです。もしstd::matrix<matrix>などのような別のヘッダで定義されていたとしても問題はあります。なぜならユーザがこのヘッダをインクルードする前に、どのヘッダをインクルードするかはユーザの自由だからです。

 ではどうすれば良かったのかというと、次のようにするべきでした。

// 将来のライブラリ拡張に対して安全なコード
#include <iostream>
namespace MyMatrix{
	class matrix{
		/* ... */
	};
	std::ostream& operator << (std::ostream& , const matrix& );
}
こうしておけば、MyMatrixという名前空間名さえユニークである限り、安全が保障されたコードになります。

 さて、「このような対策は面倒なだけであり、心配しすぎだ。using指令の存在意義を否定している」と言いたい方がいると思います。確かに、標準ライブラリに同じ名前のクラスが作られることを心配するのは、希なことかもしれません。そもそも、大文字を含む名前を使用していれば、少なくとも標準ライブラリと名前が衝突する事故は防げるわけです。using指令をしているのはstd名前空間だけですから、それで十分と考えられます。

 しかし、ユーザの立場に立ってみるとどうでしょう。プログラマは、少なくとも自分が書いているcppファイル(つまりヘッダファイルではない)の中であれば、好きなようにusing指令を使用できるべきです。僕はstd名前空間に対するusing指令(using namespace std)はヘッダ内で使用すべきではないが、個々のモジュールファイル内であれば、using指令を使用するかどうか選択する自由があると思います。それは、そのことが他のモジュールに影響を与えないからです。

 例えばMyMatrixライブラリを好んで使うプログラマは(matrix以外にもMyMatrix内のクラスがたくさんあるとして)matrix以外のMyMatrixのクラスを使うこともよくあるので、当然using namespace MyMatrixというusing指令を書きたくなるでしょう。さらにこのユーザは、std名前空間についてはusing指令を使用したくない事情があったとします。しかしusing namespace MyMatrixを使用すると、自動的にstd名前空間に対してもusing指令をしたのと同じになってしまいます。なぜならusing指令は再帰的に適用されるからです。というわけで、このプログラマにはMyMatrixをusing指令してstdをusing指令しない、という選択の自由は与えられないことになるのです。

 このように、ライブラリを使う立場に立ったとき、ヘッダ内のusing指令がプログラマの選択の自由の一部を奪うことになるという点は、重要だと思います。ですから、少なくともヘッダの中だけは、面倒でもusing指令を使わずにstd::をつけて記述するようにしましょう。