C++ Learning

第11回 namespace と using

C++ の 名前空間(namespace)は、関数や変数を「名札つきのフォルダ」に整理する仕組みです。std::coutstd:: の正体、using 宣言と using ディレクティブの違い、そして「using namespace std; はなぜ実務で避けられるのか」を、C 流の解決策と対比しながら整理します。

このページで押さえること
✅ 最低限ここだけ覚える
  • 名前空間 = 識別子をグループ化する フォルダ
  • 呼び出しは 名前空間::識別子
  • 標準ライブラリは全て std 名前空間
  • using namespace std; は学習用、実務では避ける
⭐ 余裕があれば読む
  • using std::cout;(using 宣言)と using namespace std;(using ディレクティブ)の違い
  • 入れ子名前空間(namespace A::B { })と alias
  • 無名名前空間(C の static 関数の代替)
  • ADL(実引数依存名前検索)の存在

1. なぜ namespace が必要か(C と比較)

C 言語では、関数・グローバル変数の名前はプログラム全体で一意でなければなりません。プロジェクトが大きくなると、自作のライブラリと外部ライブラリで同名関数を作ってしまい、リンク時に「multiple definition of …」エラーで衝突します。

C: 名前衝突問題
// graphics.c int init(void) { /* グラフィック初期化 */ } // audio.c int init(void) { /* 音声初期化 */ } // → リンク時に衝突 // multiple definition of `init`
プレフィックス(graphics_init / audio_init)で逃げるのが伝統
C++: 名前空間で解決解決
// graphics.cpp namespace graphics { int init() { /* 初期化 */ } } // audio.cpp namespace audio { int init() { /* 初期化 */ } } // 呼び出し側 graphics::init(); audio::init();
同じ init でも名前空間で区別される

C 時代の「プレフィックス命名規約」(graphics_init, audio_init)は、名前空間の手動エミュレーションです。C++ では言語機構として正式にサポートされ、フォルダ階層のように識別子を整理できます。

std/ 名前空間 ├─ cout, cin, cerr 標準入出力 ├─ vector, string, map コンテナ ├─ sort, find, accumulate アルゴリズム └─ filesystem/ 入れ子名前空間 ├─ path └─ directory_iterator

標準ライブラリは std という大きなフォルダの中に全機能が入っているイメージです。

2. namespace の定義と参照

名前空間を作るのも、参照するのも文法は素直です。

定義:namespace 名前 { … }

// my_app.hpp
namespace my_app {
    constexpr int VERSION = 2;

    int add(int a, int b) { return a + b; }

    class User { /* … */ };
}

参照:名前空間::識別子

int v = my_app::VERSION;
int s = my_app::add(1, 2);
my_app::User u;

::スコープ解決演算子と呼ばれ、「この名前空間(またはクラス)の中の」を意味します。std::cout も同じ仕組みで、「std 名前空間の cout」と読みます。

名前空間は分割定義できる: 同じ namespace my_app { … } ブロックを複数のファイル・複数の場所に書いても、すべて同じ名前空間に追加されます。標準ライブラリはこれを使って、<iostream><vector> など多数のヘッダから std に機能を足し込んでいます。

3. using 宣言と using ディレクティブ

毎回 std:: を書くのが冗長な場面があります。それを省略する仕組みが using ですが、書き方によって「取り込みの強さ」が違います。

using 宣言推奨
using std::cout; using std::endl; int main() { cout << "hello" << endl; }
cout / endl のみを取り込む
using ディレクティブ注意
using namespace std; int main() { cout << "hello" << endl; }
std の 全識別子を取り込む

using 宣言(using std::cout;

「この識別子だけを今のスコープに取り込む」という限定的な指定です。取り込む識別子が明示されているため、何が入るかが一目で分かります。

using ディレクティブ(using namespace std;

その名前空間の全識別子を取り込みます。便利ですが、std には count, distance, size, find など一般的な名前が大量にあるため、自作の関数や他ライブラリと衝突する危険があります。

絶対にやってはいけない: using namespace std;ヘッダファイル(.h / .hpp)のグローバルスコープに書くこと。そのヘッダを #include したすべてのファイルが影響を受け、ユーザーは知らないうちに名前空間が汚染されます。

使い分けの判断表

場面毎回 std::using 宣言using ディレクティブ
ヘッダファイル(.hpp)の
グローバルスコープ
○ 推奨 × 避ける × 厳禁
cpp ファイルの
グローバルスコープ
○ 安全 △ 限定的に可 × 避ける
関数の中 ○ 安全 ○ よく使う △ 短い関数なら可
競プロ・学習用の
使い捨てコード
△ 冗長 ○ 妥当 ○ 妥当
本サイトの方針: 学習者が「どこからきた識別子か」を常に意識できるよう、原則として std:: を毎回書きます。長いコード例で読みづらくなる場面のみ、関数スコープに using std::cout; のような using 宣言を入れる方針です。
余裕があれば読む ― ここから先は応用
最低限は上の 3 節で完結。ここからは入れ子・alias・無名名前空間など、中規模以上のプロジェクトで出てくる話です。

4. 入れ子名前空間と namespace alias

入れ子名前空間

名前空間はフォルダのように入れ子にできます。標準ライブラリの std::filesystemstd::ranges::views も入れ子名前空間です。

// 従来の書き方
namespace my_app {
    namespace network {
        void connect() { /* … */ }
    }
}
my_app::network::connect();

// C++17 以降の短縮記法
namespace my_app::network {
    void connect() { /* … */ }
}

namespace alias

長い名前空間に別名を付けて、参照を短くできます。

// std::filesystem を fs として使う(実務でよくある書き方)
namespace fs = std::filesystem;

fs::create_directory("work");
for (auto& e : fs::directory_iterator(".")) {
    std::cout << e.path() << "\n";
}

alias は関数スコープ・名前空間スコープのどちらにも書けます。長い識別子を毎回書く負担を減らしつつ、using namespace のような名前空間汚染も起きないため、長い名前空間と付き合う最良の方法です。

5. 無名名前空間(static の代替)

C で「ファイル内だけで使う関数・変数」を作るときは static を付けていました。C++ にはより現代的な無名名前空間という仕組みがあり、こちらが推奨されます。

C: ファイルローカルC 流
// helper.c static int internal_helper(int x) { return x * 2; } static int g_counter = 0; int api() { return internal_helper(g_counter++); }
static でファイル外から見えなくする
C++: 無名名前空間C++ 推奨
// helper.cpp namespace { int internal_helper(int x) { return x * 2; } int g_counter = 0; } int api() { return internal_helper(g_counter++); }
無名名前空間はそのファイル限定

無名名前空間に入れた識別子は、コンパイラが自動的にそのファイル固有の名前空間名を割り当てます。結果としてファイル外からは参照できなくなり、static と同じ「内部リンケージ」効果が得られます。

なぜ無名名前空間が推奨か: ① 関数だけでなくクラスや型エイリアスにも適用できる(static class は書けない)、② 複数の識別子をブロックでまとめて隠蔽できる、③ 「これらはファイル内限定」という意図がブロック単位で明示できる、という利点があります。

6. 使い分けのまとめ

覚える結論はシンプルです。次の 4 つだけ押さえれば、本サイトの全章のコードが読めるようになります。

次章へ: ここまでで C++ の「呼び出し側の流儀」が揃いました。次は C++ で新しく加わる 参照渡し(T&を扱います。C のポインタ渡しをどう置き換えるか、関数引数の書き方を中心に整理します。
広告スペース

確認クイズ

ここまでの理解を 3 問で確認してみましょう。

Q1. std::coutstd:: が意味するものは?

標準テンプレートの略で、テンプレート版の cout を呼び出している
std という名前空間の中の cout を指す(スコープ解決)
static の略で、静的な cout を呼び出している
stream descriptor の略で、出力先を std にする
std は標準ライブラリ全体が入っている名前空間で、:: はスコープ解決演算子です。std::cout は「std 名前空間の中の cout」を意味します。

Q2. using namespace std; をヘッダファイルのグローバルスコープに書くのが厳禁とされる主な理由は?

コンパイル速度が極端に遅くなるため
そのヘッダを include した全ファイルに名前空間汚染が伝播するため
C++ の規格で禁止されており、コンパイルエラーになるため
バイナリサイズが大きくなるため
ヘッダファイルに書くと、それを include したすべての翻訳単位(cpp ファイル)で std 名前空間が取り込まれてしまいます。利用者は知らないうちに std::count と自作 count が衝突する、という事故が起きます。コンパイルエラーにはなりませんが、バグの温床になります。

Q3. C の static 関数の C++ における推奨される代替は?

private を関数の前に書く
const を関数の前に書く
namespace { … }(無名名前空間)に入れる
using static を宣言する
無名名前空間 namespace { … } はコンパイラが内部的に固有の名前空間名を割り当て、結果としてそのファイル外から参照不可になります。関数だけでなくクラスや型エイリアスにも適用できる点で static より柔軟です。なお C++ では関数に対する static も引き続き合法です。
この記事をシェア