名前空間
名前空間は、大規模プロジェクトにおける名前の衝突を防ぐための方法を提供します。
名前空間ブロック内で宣言されたエンティティは、名前空間スコープに配置され、他のスコープで同じ名前のエンティティと誤解されるのを防ぎます。
すべての名前空間ブロックの外で宣言されたエンティティは、グローバル名前空間に属します。グローバル名前空間はグローバルスコープに属し、先頭に::を付けて明示的に参照できます。宣言はありませんが、グローバル名前空間は無名名前空間ではありません。
同じ名前を持つ複数の名前空間ブロックが許可されます。これらのブロック内のすべての宣言は、同じ名前空間スコープ内で宣言されます。
目次 |
[編集] 構文
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-body内に現れる宣言(ネストされた名前空間定義を含む)によって導入されたすべての名前は、この名前空間定義が元の名前空間定義(identifierを導入したもの)であろうと、拡張名前空間定義(既に定義された名前空間を「再開」したもの)であろうと、名前空間identifierのメンバーになります。
名前空間本体内で宣言された名前空間メンバーは、明示的な修飾を使用してその外部で定義または再宣言することができます。
namespace Q { namespace V // V is a member of Q, and is fully defined within Q { // namespace Q::V { // C++17 alternative to the lines above class C { void m(); }; // C is a member of V and is fully defined within V // C::m is only declared void f(); // f is a member of V, but is only declared here } void V::f() // definition of V's member f outside of V // f's enclosing namespaces are still the global namespace, Q, and Q::V { extern void h(); // This declares ::Q::V::h } void V::C::m() // definition of V::C::m outside of the namespace (and the class body) // enclosing namespaces are the global namespace, Q, and Q::V {} }
名前空間外での定義および再宣言は、以下の条件下でのみ許可されます。
- 宣言ポイントの後であること。
- 名前空間スコープであること。
- 元の名前空間を囲む名前空間(グローバル名前空間を含む)内であること。
また、修飾子付きID構文を使用しなければなりません。
namespace Q { namespace V // original-namespace-definition for V { void f(); // declaration of Q::V::f } void V::f() {} // OK void V::g() {} // Error: g() is not yet a member of V namespace V // extension-namespace-definition for V { void g(); // declaration of Q::V::g } } namespace R // not an enclosing namespace for Q { void Q::V::g() {} // Error: cannot define Q::V::g inside R } void Q::V::g() {} // OK: global namespace encloses Q
非ローカルクラスX内でのfriend宣言によって導入された名前は、Xを最も内側に囲む名前空間のメンバーになりますが、一致する宣言がクラス定義の前または後で名前空間スコープで提供されない限り、通常の名前探索(非修飾も修飾も)には可視になりません。このような名前は、名前空間とクラスの両方を考慮するADLを通じて見つけることができます。
このようなフレンド宣言は、名前が以前に宣言された名前と衝突するかどうかを決定する際に、最も内側の囲む名前空間のみを考慮します。
void h(int); namespace A { class X { friend void f(X); // A::f is a friend class Y { friend void g(); // A::g is a friend friend void h(int); // A::h is a friend, no conflict with ::h }; }; // A::f, A::g and A::h are not visible at namespace scope // even though they are members of the namespace A X x; void g() // definition of A::g { f(x); // A::X::f is found through ADL } void f(X) {} // definition of A::f void h(int) {} // definition of A::h // A::f, A::g and A::h are now visible at namespace scope // and they are also friends of A::X and A::X::Y }
インライン名前空間インライン名前空間は、そのoriginal-namespace-definitionにオプションのキーワード インライン名前空間のメンバーは、多くの状況(下記参照)で、それを囲む名前空間のメンバーであるかのように扱われます。このプロパティは推移的です。名前空間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
|
- | (C++11以降) 存在する場合、これをインライン名前空間にします。 |
| attr | - | (C++17以降) 任意の数の属性のオプションのシーケンス |
この定義は、一意の名前を持つ名前空間の定義と、この無名名前空間を指名する現在のスコープでのusingディレクティブとして扱われます(注: 暗黙的に追加されるusingディレクティブは、修飾名探索および非修飾名探索では名前空間を利用可能にしますが、引数依存探索では利用可能にしません)。一意の名前はプログラム全体で一意ですが、翻訳単位内では各無名名前空間定義は同じ一意の名前にマッピングされます。同じスコープ内の複数の無名名前空間定義は、同じ無名名前空間を示します。
namespace { int i; // defines ::(unique)::i } void f() { i++; // increments ::(unique)::i } namespace A { namespace { int i; // A::(unique)::i int j; // A::(unique)::j } void g() { i++; } // A::(unique)::i++ } using namespace A; // introduces all names from A into global namespace void h() { i++; // error: ::(unique)::i and ::A::(unique)::i are both in scope A::i++; // ok, increments ::A::(unique)::i j++; // ok, increments ::A::(unique)::j }
|
無名名前空間内の名前は外部リンケージで宣言されていても、その名前空間名が一意であるため、他の翻訳単位からアクセスすることはできません。 |
(C++11まで) |
|
無名名前空間、および無名名前空間内で直接的または間接的に宣言されたすべての名前空間は内部リンケージを持ちます。これは、無名名前空間内で宣言された任意の名前が内部リンケージを持つことを意味します。 |
(C++11以降) |
[編集] Using宣言
このusing宣言が現れる宣言領域に、他の場所で定義されている名前を導入します。
using typename(省略可能) nested-name-specifier unqualified-id ; |
(C++17まで) | ||||||||
using declarator-list ; |
(C++17以降) | ||||||||
typename
|
- | using宣言が基底クラスからクラステンプレートにメンバー型を導入する場合、依存名を解決するためにキーワードtypenameが必要に応じて使用されることがあります。 |
| nested-name-specifier | - | 名前とスコープ解決演算子::のシーケンスで、スコープ解決演算子で終わるもの。単一の::はグローバル名前空間を参照します。 |
| unqualified-id | - | id式 |
| declarator-list | - | typename(省略可能) nested-name-specifier unqualified-idの形式の1つ以上の宣言子のコンマ区切りリスト。宣言子の後に省略記号を付けてパック展開を示すことができますが、この形式は派生クラス定義でのみ意味を持ちます。 |
Using宣言は、名前空間メンバーを他の名前空間やブロックスコープに導入したり、基底クラスメンバーを派生クラス定義に導入したりするために使用できます。または、列挙子を名前空間、ブロック、およびクラススコープに導入するために使用できます(C++20以降)。
|
複数のusing-declaratorを持つusing宣言は、1つのusing-declaratorを持つ対応するusing宣言のシーケンスと同等です。 |
(C++17以降) |
派生クラス定義での使用については、using declarationを参照してください。
using宣言によって名前空間スコープに導入された名前は、他のスコープからの修飾探索を含む、他の名前と同様に使用できます。
void f(); namespace A { void g(); } namespace X { using ::f; // global f is now visible as ::X::f using A::g; // A::g is now visible as ::X::g using A::g, A::g; // (C++17) OK: double declaration allowed at namespace scope } void h() { X::f(); // calls ::f X::g(); // calls A::g }
using宣言が名前空間からメンバーを取り込むために使用された後、名前空間が拡張され、同じ名前の追加の宣言が導入された場合、それらの追加の宣言はusing宣言を通じて可視にはなりません(usingディレクティブとは対照的に)。ただし、using宣言がクラステンプレートを指名する場合が1つの例外です。後から導入された部分特殊化は、その探索がプライマリテンプレートを通じて行われるため、実質的に可視になります。
namespace A { void f(int); } using A::f; // ::f is now a synonym for A::f(int) namespace A // namespace extension { void f(char); // does not change what ::f means } void foo() { f('a'); // calls f(int), even though f(char) exists. } void bar() { using A::f; // this f is a synonym for both A::f(int) and A::f(char) f('a'); // calls f(char) }
Using宣言はtemplate-idや名前空間、またはスコープ付き列挙子(C++20まで)を指名できません。using宣言内の各宣言子は、1つだけの名前を導入します。例えば、列挙型のusing宣言は、その列挙子を導入しません。
同じ名前の通常の宣言、隠蔽、オーバーロード規則に関するすべての制限は、using宣言にも適用されます。
namespace A { int x; } namespace B { int i; struct g {}; struct x {}; void f(int); void f(double); void g(char); // OK: function name g hides struct g } void func() { int i; using B::i; // error: i declared twice void f(char); using B::f; // OK: f(char), f(int), f(double) are overloads f(3.5); // calls B::f(double) using B::g; g('a'); // calls B::g(char) struct g g1; // declares g1 to have type struct B::g using B::x; using A::x; // OK: hides struct B::x x = 99; // assigns to A::x struct x x1; // declares x1 to have type struct B::x }
using宣言によって関数が導入された場合、同じ名前とパラメータリストを持つ関数を宣言することは不正です(その宣言が同じ関数に対するものでない限り)。using宣言によって関数テンプレートが導入された場合、同じ名前、パラメータ型リスト、戻り型、およびテンプレートパラメータリストを持つ関数テンプレートを宣言することは不正です。2つの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; // introduces B::f(int), B::f(double) using C::f; // introduces C::f(int), C::f(double), and C::f(char) f('h'); // calls C::f(char) f(1); // error: B::f(int) or C::f(int)? void f(int); // error: f(int) conflicts with C::f(int) and B::f(int) }
エンティティがいくつかの内部名前空間で宣言されているが定義されておらず、その後、外側の名前空間でusing宣言を通じて宣言され、その後、同じ非修飾名を持つ定義が外側の名前空間に現れる場合、その定義は外側の名前空間のメンバーであり、using宣言と衝突します。
namespace X { namespace M { void g(); // declares, but doesn't define X::M::g() } using M::g; void g(); // Error: attempt to declare X::g which conflicts with X::M::g() }
より一般的に、任意の名前空間スコープに現れ、非修飾識別子を使用して名前を導入する宣言は、常にそれが存在する名前空間にメンバーを導入し、他の名前空間には導入しません。例外は、インライン名前空間で定義されたプライマリテンプレートの明示的なインスタンス化と明示的な特殊化です。これらは新しい名前を導入しないため、外側の名前空間で非修飾IDを使用できます。
[編集] Usingディレクティブ
usingディレクティブは、次の構文を持つブロック宣言です。
attr (省略可能) using namespace nested-name-specifier (省略可能) namespace-name ; |
(1) | ||||||||
| attr | - | (C++11以降) このusingディレクティブに適用される任意の数の属性 |
| nested-name-specifier | - | 名前とスコープ解決演算子::のシーケンスで、スコープ解決演算子で終わるもの。単一の::はグローバル名前空間を参照します。このシーケンス内の名前を探索するとき、探索は名前空間宣言のみを考慮します。 |
| namespace-name | - | 名前空間の名前。この名前を探索するとき、探索は名前空間宣言のみを考慮します。 |
Usingディレクティブは、名前空間スコープおよびブロックスコープでのみ許可されます。usingディレクティブの後の任意の名前の非修飾名探索の観点からは、それが現れるスコープの終わりまで、namespace-nameからのすべての名前は、usingディレクティブとnamespace-nameの両方を含む最も近い外側の名前空間で宣言されたかのように可視です。
Usingディレクティブは、それが現れる宣言領域に名前を追加しません(using宣言とは異なり)、したがって、同じ名前が宣言されるのを防ぎません。
Usingディレクティブは、非修飾探索の目的で推移的です。あるスコープにnamespace-nameを指名するusingディレクティブがあり、そのnamespace-name自体がいくつかのnamespace-name-2に対するusingディレクティブを含んでいる場合、その効果は、2番目の名前空間からのusingディレクティブが最初の名前空間内に現れるかのようなものです。これらの推移的な名前空間が現れる順序は、名前探索に影響を与えません。
namespace A { int i; } namespace B { int i; int j; namespace C { namespace D { using namespace A; // Names from A are "injected" into D. // Unqualified lookup within D considers these names to have the same // scope as the global scope (e.g. for the purposes of name hiding). // Qualified lookup referring to D (D::name for some name) // will find the same name as unqualified lookup within D. int j; int k; int a = i; // i is B::i, because A::i is hidden by B::i int b = ::i; // error: there is still no i in the global namespace } using namespace D; // names from D and A are injected into C int k = 89; // OK to declare name identical to one introduced by a using int l = k; // ambiguous: C::k or D::k int m = i; // ok: B::i hides A::i int n = j; // ok: D::j hides B::j } } // These are all equivalent definitions: 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; // introduces D::d1, D::f, D::d2, D::f, // E::e, and E::f into global namespace! int d1; // OK: no conflict with D::d1 when declaring namespace E { int e; void f(int); } namespace D // namespace extension { int d2; using namespace E; // transitive using-directive void f(int); } void f() { d1++; // error: ambiguous ::d1 or D::d1? ::d1++; // OK D::d1++; // OK d2++; // OK, d2 is D::d2 e++; // OK: e is E::e due to transitive using f(1); // error: ambiguous: D::f(int) or E::f(int)? f('a'); // OK: the only f(char) is D::f(char) }
[編集] 注釈
任意の名前空間スコープでのusingディレクティブusing namespace std;は、名前空間stdからのすべての名前をグローバル名前空間に導入します(グローバル名前空間がstdとユーザー宣言された任意の名前空間の両方を含む最も近い名前空間であるため)。これは望ましくない名前の衝突につながる可能性があります。このことや他の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の解決が クラスおよび列挙型宣言に適用されていませんでした。 |
適用済み |
[編集] 関連項目
| 名前空間エイリアス | 既存の名前空間のエイリアスを作成します |