namespace(名前空間)
『C++』の名前空間(namespace)は、識別子の衝突を防ぐためのスコープ管理機能です。大規模なプロジェクトやライブラリを組み合わせる場面で、同名の関数・クラス・変数が衝突しないよう区切り線を引く役割を担います。using ディレクティブや using 宣言を使うと、名前空間のプレフィックスを省略して記述できます。
構文
// ========================================
// 名前空間の基本構文
// ========================================
// 名前空間の定義です
namespace MyNamespace {
int value = 42;
void greet() {
// 処理を記述します
}
}
// 完全修飾名(::)でアクセスします
MyNamespace::greet();
int x = MyNamespace::value;
// using 宣言:特定の識別子だけを現在のスコープに取り込みます
using MyNamespace::greet;
greet(); // プレフィックスなしで呼べます
// using ディレクティブ:名前空間全体を現在のスコープに展開します
using namespace MyNamespace;
greet();
int y = value;
// 名前空間は分割して定義できます(同一ファイル内・複数ファイル間どちらも可)
namespace MyNamespace {
void goodbye() {} // 後から追加した定義です
}
// 入れ子の名前空間です
namespace Outer {
namespace Inner {
void func() {}
}
}
Outer::Inner::func();
// C++17 以降の入れ子記法(参考)
// namespace Outer::Inner { void func() {} }
// 無名名前空間:そのファイル内にのみ有効なプライベートスコープです
namespace {
int secret = 100; // 他のファイルからは見えません
}
構文一覧
| 構文・概念 | 概要 |
|---|---|
| namespace 名前 { } | 名前空間を定義します。同じ名前の名前空間を複数箇所に分割して書くことができ、すべて同一名前空間として結合されます。 |
| 名前空間::識別子 | 完全修飾名でアクセスします。どの名前空間の識別子かを明示でき、可読性と安全性が高まります。 |
| using 名前空間::識別子 | using 宣言です。指定した識別子だけを現在のスコープに取り込み、プレフィックスなしで使えます。影響範囲が狭く、よく使われます。 |
| using namespace 名前空間 | using ディレクティブです。名前空間内のすべての識別子を現在のスコープに展開します。ヘッダファイルのグローバルスコープへの記述は避けられることが多いです。 |
| namespace { } | 無名(匿名)名前空間です。そのファイル内にのみ有効なスコープを作り、他のファイルからの可視性を遮断します。C の static と同等の効果があります。 |
| namespace エイリアス = 名前空間 | 名前空間に別名を付けます。長い入れ子の名前空間を短い名前で参照できるようになります。 |
サンプルコード
steins_namespace.cpp
// ========================================
// steins_namespace.cpp
// Steins;Gate の登場人物をもとに名前空間の
// 定義・完全修飾名・using ディレクティブ・
// 無名名前空間を実践するサンプルです
// ========================================
#include <iostream>
#include <string>
// ========================================
// 名前空間 lab_member
// ラボメンの情報を管理する名前空間です
// ========================================
namespace lab_member {
// ラボメンのコードネームを保持する構造体です
struct Member {
std::string realName; // 本名です
std::string codeName; // ラボメン番号と呼び名です
};
// ラボメン 001 : 岡部倫太郎です
Member okabe = { "岡部倫太郎", "ラボメン 001 / 鳳凰院凶真" };
// ラボメン 002 : 牧瀬紅莉栖です
Member kurisu = { "牧瀬紅莉栖", "ラボメン 002 / クリスティーナ" };
// ラボメン 003 : 椎名まゆりです
Member mayuri = { "椎名まゆり", "ラボメン 003 / まゆしぃ" };
// ラボメンを紹介する関数です
void introduce(const Member& m) {
std::cout << " 本名: " << m.realName
<< " コードネーム: " << m.codeName << std::endl;
}
} // namespace lab_member
// ========================================
// 名前空間 time_travel
// タイムトラベル関連の機能を管理する名前空間です
// ========================================
namespace time_travel {
// 世界線の情報を保持する構造体です
struct WorldLine {
std::string name; // 世界線の名称です
double divergence; // 世界線変動率です
};
// 代表的な世界線データです
WorldLine alpha = { "α世界線", 0.334910 };
WorldLine beta = { "β世界線", 1.130205 };
WorldLine steins_gate = { "Steins;Gate", 1.048596 };
// 世界線情報を表示する関数です
void showWorldLine(const WorldLine& wl) {
std::cout << " " << wl.name
<< " (divergence: " << wl.divergence << ")" << std::endl;
}
} // namespace time_travel
// ========================================
// 無名名前空間
// このファイル内だけで有効なプライベートな定数です
// 他のファイルからは一切参照できません
// ========================================
namespace {
const std::string LAB_NAME = "未来ガジェット研究所";
const int LAB_ROOM_NO = 204;
}
// ========================================
// 名前空間エイリアス
// time_travel という長い名前に短い別名を付けます
// ========================================
namespace tt = time_travel;
int main() {
// ----------------------------------------
// 1. 完全修飾名でアクセスします
// ----------------------------------------
std::cout << "=== ラボメン一覧(完全修飾名) ===" << std::endl;
lab_member::introduce(lab_member::okabe);
lab_member::introduce(lab_member::kurisu);
lab_member::introduce(lab_member::mayuri);
std::cout << std::endl;
// ----------------------------------------
// 2. using 宣言:特定の識別子だけを取り込みます
// ブロックスコープ内に限定すると影響範囲を絞れます
// ----------------------------------------
{
using lab_member::introduce;
using lab_member::mayuri;
// ラボメン 005 : 阿万音鈴羽をローカルで定義します
lab_member::Member suzuha = { "阿万音鈴羽", "ラボメン 005 / 未来のラボメン" };
std::cout << "=== using 宣言のスコープ内 ===" << std::endl;
introduce(mayuri); // プレフィックスなしで呼べます
introduce(suzuha);
std::cout << std::endl;
}
// このブロックの外では introduce は解決されません
// ----------------------------------------
// 3. 名前空間エイリアスを使ってアクセスします
// ----------------------------------------
std::cout << "=== 世界線情報(エイリアス tt::) ===" << std::endl;
tt::showWorldLine(tt::alpha);
tt::showWorldLine(tt::beta);
tt::showWorldLine(tt::steins_gate);
std::cout << std::endl;
// ----------------------------------------
// 4. 無名名前空間の定数を使います
// 同ファイル内なので :: なしで参照できます
// ----------------------------------------
std::cout << "=== ラボ情報(無名名前空間) ===" << std::endl;
std::cout << " ラボ名: " << LAB_NAME << std::endl;
std::cout << " 部屋番号: " << LAB_ROOM_NO << std::endl;
std::cout << std::endl;
// ----------------------------------------
// 5. using ディレクティブで名前空間全体を展開します
// スコープを絞った using namespace の実例です
// ----------------------------------------
{
using namespace lab_member;
// 橋田至をローカルで定義します
Member hashida = { "橋田至", "ラボメン 004 / ダル / スーパーハカー" };
std::cout << "=== using namespace のスコープ内 ===" << std::endl;
introduce(okabe); // lab_member:: なしで使えます
introduce(hashida);
}
return 0;
}
# コンパイルします g++ -std=c++11 steins_namespace.cpp -o steins_namespace # 実行します ./steins_namespace === ラボメン一覧(完全修飾名) === 本名: 岡部倫太郎 コードネーム: ラボメン 001 / 鳳凰院凶真 本名: 牧瀬紅莉栖 コードネーム: ラボメン 002 / クリスティーナ 本名: 椎名まゆり コードネーム: ラボメン 003 / まゆしぃ === using 宣言のスコープ内 === 本名: 椎名まゆり コードネーム: ラボメン 003 / まゆしぃ 本名: 阿万音鈴羽 コードネーム: ラボメン 005 / 未来のラボメン === 世界線情報(エイリアス tt::) === α世界線 (divergence: 0.33491) β世界線 (divergence: 1.1302) Steins;Gate (divergence: 1.0486) === ラボ情報(無名名前空間) === ラボ名: 未来ガジェット研究所 部屋番号: 204 === using namespace のスコープ内 === 本名: 岡部倫太郎 コードネーム: ラボメン 001 / 鳳凰院凶真 本名: 橋田至 コードネーム: ラボメン 004 / ダル / スーパーハカー
よくあるミス: ヘッダファイルのグローバルスコープで using namespace を書く
ヘッダファイルのグローバルスコープに using namespace std; を書くと、そのヘッダを #include したすべてのファイルに展開が波及し、名前の衝突が起きやすくなります。
// NG: ヘッダファイルのグローバルスコープに書くのは避けられることが多いです
// lab_member.h
#pragma once
#include <string>
using namespace std; // このヘッダをインクルードした全ファイルに展開されます
struct Member {
string realName; // string が std:: なしで使えますが名前衝突のリスクがあります
};
OK: ヘッダ内では完全修飾名を使うか、using 宣言を関数スコープ内に限定します。
// OK: 完全修飾名を使います
// lab_member.h
#pragma once
#include <string>
struct Member {
std::string realName; // 完全修飾名で記述します
std::string codeName;
};
よくあるミス: 名前空間を using で展開したスコープ外でも有効と思い込む
using 宣言・using namespace ディレクティブはそれが書かれたスコープ内にのみ有効です。ブロックスコープの外に出ると有効でなくなります。
// NG: スコープ外で using の効果を期待してしまう例です
{
using lab_member::introduce;
introduce(okabe); // ここは OK です
}
// introduce(okabe); // コンパイルエラー: このスコープでは introduce が解決されません
OK: 使いたいスコープに using 宣言を書きます。
// OK: スコープを意識して using 宣言を書きます
void showMembers() {
using lab_member::introduce;
using lab_member::okabe;
introduce(okabe); // この関数内でのみ有効です
}
概要
名前空間は識別子の衝突を防ぐためのスコープ管理機構であり、特に複数のライブラリを組み合わせる大規模開発で欠かせない機能です。using 宣言は特定の識別子だけを現在のスコープに取り込むため影響範囲が狭く、よく使われます。一方、using namespace ディレクティブは名前空間全体を展開するため便利ですが、グローバルスコープで多用すると他の名前空間の識別子と衝突するリスクがあります。ヘッダファイルのグローバルスコープへの using namespace std; の記述は避けられることが多いです。無名名前空間はそのファイル内だけで有効なプライベートスコープを作るもので、C の static によるファイルスコープと同等の効果が得られます。名前空間のエイリアス(namespace tt = time_travel;)を使うと、入れ子が深い名前空間を短い名前で参照でき、可読性が向上します。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。