using namespace std; の危険性と注意点・代替案


便利なのでつい使ってしまう

C++では名前空間の面倒な修飾を省略するためにmain関数の外でusing namespace std;という記述をすることがあると思います。これによって、std::coutという記述をいつでも手軽にcoutと書けるようになり、大変便利に使えます。

using namespace std;

int  main() { cout << "便利"; }
void test() { cout << "便利"; }

名前空間が汚染される

ただstd::stringstd::vector等のクラスも同様にstringvectorという省略された形で記述出来てしまう点が難点です。同一名前空間にstringという同名クラスが別に存在していた場合、クラス名の重複が発生してしまいます(名前衝突)。

using namespace std;      /* I have a std::string      */
using namespace std_neue; /* I have a std_neue::string */

string str = "ugh!"; // エラー:Reference to 'string' is ambiguous

もちろんユーザ定義型ともバッティングします。

using namespace std;
struct string {};
string str; // Reference to 'string' is ambiguous

また自身が定義した同名型との区別が曖昧になるというややこしい問題もあります。

namespace nm {
   struct string {};
   using namespace std;
   string str; // std::stringではない(ややこしい)
}

この曖昧さは勘違いやミスの原因にもなりやすく危険です。そのためグローバル空間や名前空間でのusing namespace stdには十分注意をする必要があります。

スポンサーリンク

using namespace std 利用時の注意

using namespace stdを用いる場合は以下の特性を理解した上で使うようにしましょう。

グローバルスコープ時/ファイルスコープ時

using namespace std;

struct string {};
template<class T> T move(T v) { return v; }

int main() {
   string t{};   // きちんとエラーになる(Reference to 'string' is ambiguous)
   move(9);      // きちんとエラーになる(Call to 'move' is ambiguous)
   std::move(9); // std::moveを明示的に呼び出せば問題ない
   ::move(9);    // ユーザ定義側が呼ばれる
   ::string s{}; // ユーザ定義側が使われる
}

名前空間内

using namespace std;

namespace nm {
   struct string {};
   template<class T> T move(T v) { return v; }
   
   void main() {
      move(9);      // ユーザ定義側が呼ばれる
      string t{};   // ユーザ定義側が使われる
      ::move(9);    // std::moveが呼ばれる
      ::string u{}; // std::stringが使われる
   }
}

名前空間内でユーザ定義を行った場合には、グローバルスコープ時/ファイルスコープ時のようなエラーは発生しませんが、名前解決の方法が変わります。コードをリファクタリングしたり、移植したりする際にはこれらの違いや特性に十分注意して行う必要があります。

代替案

個別にusingする

以下のように必要な物のみを定義するようにすれば、名前空間を無駄に多く汚さずに済みます。

using std::cout;
using std::string;
using strings = std::vector<std::string>;
template<typename T> using vector = std::vector<T>;

使い方やより詳しい説明は以下のページで解説していますので参考にしてください。

>> 関数やクラスを個別にusingする方法

限定的にusing namespace stdする

どうしてもstd内のメンバを一括で利用できるようにしたいような場合は、関数内や名前空間内などの個別のローカル空間で利用すると比較的安全です。

関数内で利用する

関数内で利用すれば、関数内でのみ全てのライブラリ関数/クラスを利用することが可能になります。

// using namespace std; // 危険(グローバル空間に侵食する)
int main() {
   using namespace std; // この程度の処理なら安全
   cout << 99 << endl;
}

ブロックスコープで利用する

if文やfor文、ブロック文等のブロックスコープ内で利用することも出来ます。

int main() {
   if (true) {
       using namespace std;
       cout << string("a");
   }
   {
      using namespace std;
      cout << string("a");
   }
   cout << string("a"); /// エラー:Use of undeclared identifier 'cout', 'string'
}

特定のタイミングで利用する

int main() {
   cout << string("a"); // エラー
   using namespace std;
   cout << string("a"); // エラーにならない
}

名前空間内で利用する

独自の名前空間で利用した場合、空間内で定義されたの全ての関数/クラスでライブラリの利用が可能になります。

namespace lib {
   using namespace std; // それなりに安全
   void print(string s) {
      cout << s << endl;
   }
}

ただし空間内のコードが複雑で膨大になればなるほど、先程挙げた名前衝突やあまいさのリスクが高まります。

独自定義の名前空間にも注意する

using namespace std;を隠蔽した独自定義の名前空間に対して、usingディレクティブを用いる際にも注意が必要です。

以下のケースではlibのメンバだけでなく、連鎖的にstdのメンバもファイルスコープやグローバルスコープへ汚染します。

namespace lib { using namespace std; }
using namespace lib;

int main() {
   cout << string("stdのメンバも汚染してる");
}

無名名前空間では危険

無名名前空間/匿名名前空間の場合はファイルスコープへの汚染を引き起こしてしまうため注意してください。

namespace {
   using namespace std; // 危険
}
string s; // 使用出来てしまう

広告