Namespaces
名前空間は、大規模プロジェクトでの名前の衝突を防ぐための方法を提供します。
名前空間ブロック内で宣言されたエンティティは、名前空間スコープに配置され、他のスコープで同じ名前を持つエンティティと誤って認識されるのを防ぎます。
すべての名前空間ブロックの外側で宣言されたエンティティは、
グローバル名前空間
に属します。グローバル名前空間は
グローバルスコープ
に属し、先頭に
::
を付けることで明示的に参照できます。宣言を持たないものの、グローバル名前空間は
無名名前空間
ではありません。
同じ名前を持つ複数の名前空間ブロックが許可されます。これらのブロック内のすべての宣言は、同じ名前空間スコープで宣言されます。
目次 |
構文
namespace
ns-name
{
declarations
}
|
(1) | ||||||||
inline
namespace
ns-name
{
declarations
}
|
(2) | (C++11以降) | |||||||
namespace
{
declarations
}
|
(3) | ||||||||
ns-name
::
member-name
|
(4) | ||||||||
using
namespace
ns-name
;
|
(5) | ||||||||
using
ns-name
::
member-name
;
|
(6) | ||||||||
namespace
name
=
qualified-namespace
;
|
(7) | ||||||||
namespace
ns-name
::
member-name
{
declarations
}
|
(8) | (C++17以降) | |||||||
namespace
ns-name
::
inline
member-name
{
declarations
}
|
(9) | (C++20以降) | |||||||
説明
名前空間
inline
(オプション)
namespace
attr
(オプション)
identifier
{
namespace-body
}
|
|||||||||
inline
|
- |
(C++11以降)
指定された場合、これをインライン名前空間とする(下記参照)。
original-namespace-definition
で
inline
が使用されなかった場合、
extension-namespace-definition
では指定できない
|
||
| attr | - | (C++17以降) 任意の数の 属性 のオプションのシーケンス | ||
| identifier | - |
以下のいずれか:
|
||
| namespace-body | - | 任意の種類の 宣言 (クラスおよび関数定義、ネストされた名前空間を含む)の空でもよいシーケンス |
名前空間の定義は、グローバルスコープを含む名前空間スコープでのみ許可されます。
既存の名前空間を再び開くには(形式的には extension-namespace-definition となる)、名前空間定義で使用される identifier の検索が、外側の名前空間または外側の名前空間内のインライン名前空間のメンバーとして宣言された名前空間名(名前空間エイリアスではない)に解決されなければならない。
namespace-body は namespace scope を定義し、 name lookup に影響を与えます。
namespace-body 内に現れる宣言によって導入されるすべての名前は、 identifier の名前空間のメンバーとなる。 これが元の名前空間定義( identifier を導入したもの)であっても、 拡張名前空間定義(既に定義された名前空間を「再オープン」したもの)であっても同様である。
名前空間本体内部で宣言された名前空間メンバーは、明示的な修飾を使用して外部で定義または再宣言することができます
namespace Q { namespace V // VはQのメンバーであり、Q内で完全に定義されている { // namespace Q::V { // 上記行に対するC++17の代替構文 class C { void m(); }; // CはVのメンバーであり、V内で完全に定義されている // C::mは宣言のみ void f(); // fはVのメンバーだが、ここでは宣言のみ } void V::f() // VのメンバーfのV外部での定義 // fの囲んでいる名前空間は依然としてグローバル名前空間、Q、Q::V { extern void h(); // これは ::Q::V::h を宣言する } void V::C::m() // 名前空間(およびクラス本体)外部でのV::C::mの定義 // 囲んでいる名前空間はグローバル名前空間、Q、Q::V {} }
名前空間外での定義と再宣言は許可されているのは
- 宣言のポイントの後、
- 名前空間スコープで、および
- 元の名前空間を囲む名前空間内(グローバル名前空間を含む)。
また、それらは修飾ID構文を使用しなければなりません。
namespace Q { namespace V // Vのオリジナル名前空間定義 { void f(); // Q::V::fの宣言 } void V::f() {} // 有効 void V::g() {} // エラー: g()はまだVのメンバーではない namespace V // Vの拡張名前空間定義 { void g(); // Q::V::gの宣言 } } namespace R // Qを囲む名前空間ではない { void Q::V::g() {} // エラー: R内部でQ::V::gを定義できない } void Q::V::g() {} // 有効: グローバル名前空間はQを囲んでいる
非ローカルクラスX内の friend 宣言によって導入される名前は、Xの最も内側の外側の名前空間のメンバーになりますが、通常の name lookup ( unqualified および qualified の両方)では可視になりません(名前空間スコープで、クラス定義の前または後に一致する宣言が提供されない限り)。そのような名前は、名前空間とクラスの両方を考慮する ADL を通じて見つけることができます。
そのようなフレンド宣言において、名前が以前に宣言された名前と競合するかどうかを決定する際には、最も内側の囲み名前空間のみが考慮されます。
void h(int); namespace A { class X { friend void f(X); // A::f はフレンド関数 class Y { friend void g(); // A::g はフレンド関数 friend void h(int); // A::h はフレンド関数、::h との衝突なし }; }; // A::f、A::g、A::h は名前空間スコープでは不可視 // これらは名前空間 A のメンバーであるにもかかわらず X x; void g() // A::g の定義 { f(x); // ADL を通じて A::X::f が見つかる } void f(X) {} // A::f の定義 void h(int) {} // A::h の定義 // A::f、A::g、A::h は名前空間スコープで可視となり // また A::X および A::X::Y のフレンドでもある }
インライン名前空間
インライン名前空間は、その
元の名前空間定義
でオプションのキーワード
インライン名前空間のメンバーは、多くの状況(以下に列挙)で、それを囲む名前空間のメンバーであるかのように扱われます。この特性は推移的です:名前空間Nがインライン名前空間Mを含み、Mがさらにインライン名前空間Oを含む場合、OのメンバーはMまたはNのメンバーであるかのように使用できます。
// in C++14, std::literals and its member namespaces are inline { using namespace std::string_literals; // makes visible operator""s // from std::literals::string_literals auto str = "abc"s; } { using namespace std::literals; // makes visible both // std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s auto str = "abc"s; auto min = 60s; } { using std::operator""s; // makes both std::literals::string_literals::operator""s // and std::literals::chrono_literals::operator""s visible auto str = "abc"s; auto min = 60s; } 注:特殊化に関するルールは、ライブラリのバージョン管理を可能にします:ライブラリテンプレートの異なる実装を異なるインライン名前空間で定義でき、同時にユーザーがプライマリテンプレートの明示的特殊化で親名前空間を拡張することができます:
このコードを実行
namespace Lib { inline namespace Lib_1 { template<typename T> class A; } template<typename T> void g(T) { /* ... */ } } /* ... */ struct MyClass { /* ... */ }; namespace Lib { template<> class A<MyClass> { /* ... */ }; } int main() { Lib::A<MyClass> a; g(a); // ok, Lib is an associated namespace of A } |
(C++11以降) |
無名名前空間
unnamed-namespace-definition は、以下の形式の名前空間定義です
inline
(オプション)
namespace
attr
(オプション)
{
namespace-body
}
|
|||||||||
inline
|
- | (since C++11) 指定された場合、この名前空間をインライン名前空間にする |
| attr | - | (since C++17) 任意の数の 属性 のオプションのシーケンス |
この定義は、一意の名前を持つ名前空間の定義として扱われ、現在のスコープでこの無名名前空間を指名する
using-directive
が含まれます(注:暗黙的に追加されたusingディレクティブは、
修飾名探索
と
非修飾名探索
では名前空間を利用可能にしますが、
実引数依存探索
では利用できません)。
一意の名前はプログラム全体でユニークですが、翻訳単位内では各無名名前空間定義は同じ一意の名前にマッピングされます:同じスコープ内の複数の無名名前空間定義は、同じ無名名前空間を表します。
namespace { int i; // ::(ユニーク)::i を定義 } void f() { i++; // ::(ユニーク)::i をインクリメント } namespace A { namespace { int i; // A::(ユニーク)::i int j; // A::(ユニーク)::j } void g() { i++; } // A::(ユニーク)::i++ } using namespace A; // Aの全名前をグローバル名前空間に導入 void h() { i++; // エラー: ::(ユニーク)::i と ::A::(ユニーク)::i が両方スコープ内 A::i++; // OK, ::A::(ユニーク)::i をインクリメント j++; // OK, ::A::(ユニーク)::j をインクリメント }
|
無名名前空間内の名前は外部リンケージで宣言される可能性があるにもかかわらず、その名前空間名が一意であるため、他の翻訳単位からアクセスすることはできません。 |
(C++11まで) |
|
無名名前空間および無名名前空間内で直接または間接的に宣言されたすべての名前空間は 内部リンケージ を持ちます。これは、無名名前空間内で宣言された任意の名前が内部リンケージを持つことを意味します。 |
(C++11以降) |
using宣言
このusing宣言が現れる宣言領域に、他の場所で定義されている名前を導入します。
using
typename
(オプション)
nested-name-specifier
unqualified-id
;
|
(C++17まで) | ||||||||
using
declarator-list
;
|
(C++17以降) | ||||||||
typename
|
- |
キーワード
typename
は、using宣言が基底クラスからクラステンプレートにメンバ型を導入する際に、
依存名
を解決するために必要に応じて使用できる
|
| nested-name-specifier | - |
名前とスコープ解決演算子
::
のシーケンスで、スコープ解決演算子で終わる。単一の
::
はグローバル名前空間を参照する
|
| unqualified-id | - | 修飾されていない 識別子式 |
| declarator-list | - |
1つ以上の宣言子のカンマ区切りリストで、形式は
typename
(オプション)
nested-name-specifier
unqualified-id
となる。宣言子の後に省略記号を付けて
パック展開
を示すことができるが、この形式は
派生クラス定義
でのみ意味を持つ
|
using宣言は、名前空間メンバーを他の名前空間やブロックスコープに導入するため、または基底クラスのメンバーを派生クラス定義に導入するために使用できます 、または enumerators を名前空間、ブロック、クラススコープに導入するために使用できます (C++20以降) 。
|
複数のusing宣言子を持つusing宣言は、1つのusing宣言子を持つ対応する一連のusing宣言と等価である。 |
(since C++17) |
派生クラス定義での使用については、 using宣言 を参照してください。
名前空間スコープにusing宣言によって導入された名前は、他の名前と同様に使用でき、他のスコープからの修飾付き検索も含まれます:
void f(); namespace A { void g(); } namespace X { using ::f; // グローバル関数fが ::X::fとして可視化される using A::g; // A::gが ::X::gとして可視化される using A::g, A::g; // (C++17) OK: 名前空間スコープでの二重宣言が許可される } void h() { X::f(); // ::fを呼び出す X::g(); // A::gを呼び出す }
using宣言を使用して名前空間からメンバーを取得した後、その名前空間が拡張され、同じ名前に対する追加の宣言が導入された場合、それらの追加の宣言はusing宣言を通じて可視にはなりません(usingディレクティブとは対照的です)。例外の一つは、using宣言がクラステンプレートを指定する場合です:後で導入された部分特殊化は実質的に可視となります。なぜなら、それらの lookup はプライマリテンプレートを通じて行われるためです。
namespace A { void f(int); } using A::f; // ::f は A::f(int) の同義語となる namespace A // 名前空間の拡張 { void f(char); // ::f の意味は変更されない } void foo() { f('a'); // f(char) が存在するにも関わらず、f(int) を呼び出す } void bar() { using A::f; // この f は A::f(int) と A::f(char) の両方の同義語となる f('a'); // f(char) を呼び出す }
using宣言は template-id や名前空間 、またはスコープ付き列挙子 (C++20まで) を指定できません。using宣言内の各宣言子は1つかつ1つのみの名前を導入します。例えば、 enum に対するusing宣言はその列挙子を一切導入しません。
同じ名前の通常の宣言、隠蔽、およびオーバーロード規則に関するすべての制限は、using宣言にも適用されます:
namespace A { int x; } namespace B { int i; struct g {}; struct x {}; void f(int); void f(double); void g(char); // OK: 関数名gが構造体gを隠蔽 } void func() { int i; using B::i; // エラー: iが二重宣言 void f(char); using B::f; // OK: f(char), f(int), f(double)がオーバーロードされる f(3.5); // B::f(double)を呼び出す using B::g; g('a'); // B::g(char)を呼び出す struct g g1; // g1をstruct B::g型として宣言 using B::x; using A::x; // OK: struct B::xを隠蔽 x = 99; // A::xに代入 struct x x1; // x1をstruct B::x型として宣言 }
using宣言によって関数が導入された場合、同じ名前と引数リストを持つ関数を宣言することは不適格です(ただし、その宣言が同じ関数である場合を除く)。using宣言によって関数テンプレートが導入された場合、同じ名前、引数型リスト、戻り値型、およびテンプレート引数リストを持つ関数テンプレートを宣言することは不適格です。 二つのusing宣言が同じ名前と引数リストを持つ関数を導入することができますが、その関数を呼び出そうとすると、プログラムは不適格となります。
namespace B { void f(int); void f(double); } namespace C { void f(int); void f(double); void f(char); } void h() { using B::f; // B::f(int) と B::f(double) を導入 using C::f; // C::f(int), C::f(double), C::f(char) を導入 f('h'); // C::f(char) を呼び出す f(1); // エラー: B::f(int) と C::f(int) のどちらを呼び出す? void f(int); // エラー: f(int) が C::f(int) および B::f(int) と競合 }
あるエンティティが内部の名前空間で宣言されているが定義されておらず、その後using宣言によって外側の名前空間で宣言され、その後同じ非修飾名を持つ定義が外側の名前空間に現れた場合、その定義は外側の名前空間のメンバーとなり、using宣言と競合します:
namespace X { namespace M { void g(); // 宣言するが、X::M::g()を定義しない } using M::g; void g(); // エラー: X::gの宣言試行はX::M::g()と競合する }
より一般的には、任意の名前空間スコープに現れ、非修飾識別子を使用して名前を導入する宣言は、常にそれが属する名前空間にメンバーを導入し、他のいずれの名前空間にも導入しません。例外は、インライン名前空間で定義されたプライマリテンプレートの明示的なインスタンス化と明示的特殊化です。これらは新しい名前を導入しないため、外側の名前空間で非修飾IDを使用することができます。
usingディレクティブ
using-directive は以下の構文を持つ block-declaration です:
attr
(オプション)
using
namespace
nested-name-specifier
(オプション)
namespace-name
;
|
(1) | ||||||||
| attr | - | (since C++11) このusingディレクティブに適用される任意の数の attributes |
| nested-name-specifier | - |
名前とスコープ解決演算子
::
のシーケンスで、スコープ解決演算子で終わる。単一の
::
はグローバル名前空間を参照する。このシーケンス内の名前を検索する際、
lookup
は名前空間宣言のみを考慮する
|
| namespace-name | - | 名前空間の名前。この名前を検索する際、 lookup は名前空間宣言のみを考慮する |
usingディレクティブは名前空間 scope およびブロックスコープでのみ許可されます。usingディレクティブ以降の任意の名前に対する unqualified name lookup の観点から、そのディレクティブが現れるスコープの終わりまで、 namespace-name からのすべての名前は、usingディレクティブと namespace-name の両方を含む最も近い外側の名前空間で宣言されたかのように可視となります。
usingディレクティブは、それが現れる宣言領域にいかなる名前も追加しません(using宣言とは異なり)、したがって同一の名前が宣言されることを妨げません。
usingディレクティブは
unqualified lookup
の目的において推移的です:あるスコープが
namespace-name
を指定するusingディレクティブを含み、その
namespace-name
自体が別の
namespace-name-2
に対するusingディレクティブを含む場合、第二の名前空間からのusingディレクティブが最初の名前空間内に現れたかのような効果があります。これらの推移的名前空間が現れる順序は名前検索に影響しません。
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; // Aの名前はDに「注入」される // D内の非修飾名探索では、これらの名前はグローバルスコープと同じスコープを持つとみなされる // (例えば、名前隠蔽の目的で) // Dを参照する修飾名探索(D::nameのような形式)は、 // D内の非修飾名探索と同じ名前を見つける int j; int k; int a = i; // iはB::i、A::iはB::iによって隠蔽されているため int b = ::i; // エラー: グローバル名前空間には依然としてiが存在しない } using namespace D; // DとAの名前がCに注入される int k = 89; // usingによって導入された名前と同一の名前を宣言可能 int l = k; // 曖昧: C::k または D::k int m = i; // OK: B::iがA::iを隠蔽 int n = j; // OK: D::jがB::jを隠蔽 } } // これらはすべて同等の定義: int t0 = B::i; int t1 = B::C::a; int t2 = B::C::D::a;
usingディレクティブを使用して特定の名前空間を指名した後、その名前空間が拡張され、追加のメンバーやusingディレクティブが追加された場合、それらの追加メンバーと追加名前空間はusingディレクティブを通じて可視となる(using宣言とは対照的)
namespace D { int d1; void f(char); } using namespace D; // D::d1、D::f、D::d2、D::f、 // E::e、E::fをグローバル名前空間に導入! int d1; // OK: 宣言時のD::d1との競合なし namespace E { int e; void f(int); } namespace D // 名前空間の拡張 { int d2; using namespace E; // 推移的なusingディレクティブ void f(int); } void f() { d1++; // エラー: 曖昧 - ::d1 と D::d1 のどちら? ::d1++; // OK D::d1++; // OK d2++; // OK、d2はD::d2 e++; // OK: eは推移的なusingによりE::e f(1); // エラー: 曖昧 - D::f(int) と E::f(int) のどちら? f('a'); // OK: 唯一のf(char)はD::f(char) }
注記
任意の名前空間スコープにおけるusingディレクティブ
using
namespace
std
;
は、名前空間
std
からのすべての名前をグローバル名前空間に導入します(グローバル名前空間は
std
とユーザー宣言された名前空間の両方を含む最も近い名前空間であるため)。これは望ましくない名前の衝突を引き起こす可能性があります。このusingディレクティブおよび他のusingディレクティブは、一般的にヘッダーファイルのファイルスコープでは悪い習慣とされています(
SF.7: ヘッダーファイルのグローバルスコープで
using
namespace
を使用しないこと
)。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_namespace_attributes
|
201411L
|
(C++17) | 属性 名前空間用 |
キーワード
例
この例は、すでに
std
名前空間で名前が付けられているクラスを作成するために名前空間を使用する方法を示しています。
#include <vector> namespace vec { template<typename T> class vector { // ... }; } // of vec int main() { std::vector<int> v1; // Standard vector. vec::vector<int> v2; // User defined vector. // v1 = v2; // Error: v1 and v2 are different object's type. { using namespace std; vector<int> v3; // Same as std::vector v1 = v3; // OK } { using vec::vector; vector<int> v4; // Same as vec::vector v2 = v4; // OK } }
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 101 | C++98 |
名前空間スコープまたはブロックスコープでの関数宣言と
using宣言によって導入された関数が同じ関数を宣言する場合、 プログラムは不適格(曖昧性なし) |
許可される |
| CWG 373 | C++98 |
usingディレクティブのオペランドの最後の名前に対してのみ
名前空間宣言を考慮したルックアップ(これは最適ではなく、 クラスは名前空間を含むことができないため) |
ルックアップの制限は
usingディレクティブのオペランド内の すべての名前に適用される |
| CWG 460 | C++98 | using宣言で名前空間を指定できた | 禁止される |
| CWG 565 | C++98 |
using宣言で同じスコープ内の別の関数と同一の関数を
導入することはできないが、この制限は関数テンプレートに 適用されなかった |
同じ制限を関数テンプレートにも
適用する |
| CWG 986 | C++98 | usingディレクティブは修飾名ルックアップに対して推移的だった | 非修飾名ルックアップに対してのみ推移的 |
| CWG 987 | C++98 |
入れ子になった名前空間で宣言されたエンティティは
外側の名前空間のメンバーでもあった |
入れ子になったスコープは除外される |
| CWG 1021 | C++98 |
using宣言によって名前空間に導入された定義を持つ
エンティティがその名前空間で定義されていると 見なされるかどうかが不明確だった |
その名前空間では定義されていない |
| CWG 1838 | C++98 |
外側の名前空間での非修飾定義が、
別の名前空間で宣言され(定義されず)usingによって 引き込まれたエンティティを定義できた |
非修飾定義は常に
自身の名前空間を 参照する |
| CWG 2155 | C++98 |
CWG issue 1838
の解決策が
クラス宣言と列挙型宣言に適用されなかった |
適用される |
関連項目
| namespace alias | 既存の名前空間のエイリアスを作成する |