名稱空間
名稱空間提供了一種方法,用於防止大型專案中的名稱衝突。
在名稱空間塊內宣告的實體被放置在名稱空間作用域中,這可以防止它們與其他作用域中同名的實體混淆。
在所有名稱空間塊之外宣告的實體屬於*全域性名稱空間*。全域性名稱空間屬於全域性作用域,並且可以使用前導::
顯式引用。儘管它沒有宣告,但全域性名稱空間不是一個無名名稱空間。
允許多個同名名稱空間塊。這些塊內的所有宣告都在相同的名稱空間作用域中宣告。
目錄 |
[編輯] 語法
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 起) 如果存在,使其成為一個內聯名稱空間(見下文)。不能出現在 *extension-namespace-definition* 中,如果 *original-namespace-definition* 沒有使用 inline 。 | ||
屬性 | - | (自 C++17 起) 任意數量的屬性的可選序列。 | ||
識別符號 | - | 要麼是
| ||
名稱空間體 | - | 可能為空的任意型別的宣告序列(包括類和函式定義以及巢狀名稱空間)。 |
名稱空間定義只允許在名稱空間作用域內,包括全域性作用域。
要重新開啟一個現有名稱空間(正式地,成為一個 *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 中的友元宣告引入的名稱成為 X 的最內層包含名稱空間的成員,但它們不會對普通名稱查詢(無論是非限定還是限定)可見,除非在名稱空間作用域中提供了匹配的宣告,無論是在類定義之前還是之後。此類名稱可以透過ADL找到,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 }
內聯名稱空間內聯名稱空間是其*原始名稱空間定義*中使用了可選關鍵字 在許多情況下(如下所列),內聯名稱空間的成員被視為其外圍名稱空間的成員。此屬性是可傳遞的:如果名稱空間 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 起) 如果存在,使其成為一個內聯名稱空間。 |
屬性 | - | (自 C++17 起) 任意數量的屬性的可選序列。 |
此定義被視為具有唯一名稱的名稱空間的定義,並在當前作用域中有一個提名此無名名稱空間的 *using-directive*(注意:隱式新增的 using-directive 使名稱空間可用於限定名查詢和非限定名查詢,但不能用於實參依賴查詢)。該唯一名稱在整個程式中是唯一的,但在一個翻譯單元內,每個無名名稱空間定義都對映到相同的唯一名稱:同一作用域中的多個無名名稱空間定義表示相同的無名名稱空間。
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 起) | ||||||||
型別名
|
- | 當 using-declaration 將基類的成員型別引入類模板時,關鍵字 typename 可用於解析依賴名稱。 |
巢狀名稱限定符 | - | 名稱序列和作用域解析運算子:: ,以作用域解析運算子結尾。單個:: 指代全域性名稱空間。 |
非限定ID | - | 一個ID 表示式 |
宣告符列表 | - | 一個或多個宣告符的逗號分隔列表,形式為 typename (可選) nested-name-specifier unqualified-id。宣告符後可以跟省略號以指示包擴充套件,儘管該形式僅在派生類定義中才有意義。 |
using-宣告可用於將名稱空間成員引入其他名稱空間和塊作用域,或將基類成員引入派生類定義,或將列舉器引入名稱空間、塊和類作用域(自 C++20 起)。
具有多個 using-宣告符的 using-宣告等價於具有一個 using-宣告符的相應 using-宣告序列。 |
(C++17 起) |
對於派生類定義中的使用,請參見using 宣告。
透過 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-宣告命名一個類模板時:稍後引入的部分特化實際上是可見的,因為它們的查詢是透過主模板進行的。
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-宣告不能命名模板-id或名稱空間,或有作用域的列舉器(直至 C++20)。using-宣告中的每個宣告符只引入一個名稱,例如,列舉的 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-宣告引入,則宣告一個同名、引數型別列表、返回型別和模板引數列表的函式模板是格式錯誤的。兩個 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-directive* 是一個具有以下語法的塊宣告:
attr (可選) using namespace nested-name-specifier (可選) namespace-name ; |
(1) | ||||||||
屬性 | - | (自 C++11 起) 適用於此 using-directive 的任意數量的屬性。 |
巢狀名稱限定符 | - | 名稱序列和作用域解析運算子:: ,以作用域解析運算子結尾。單個:: 指代全域性名稱空間。在查詢此序列中的名稱時,查詢只考慮名稱空間宣告。 |
名稱空間名稱 | - | 名稱空間的名稱。在查詢此名稱時,查詢只考慮名稱空間宣告。 |
using-指令只允許在名稱空間作用域和塊作用域中。從using-指令之後到其出現的作用域結束的任何名稱的非限定名稱查詢的角度來看,namespace-name 中的每個名稱都可見,就像它在包含 using-directive 和 namespace-name 的最近外圍名稱空間中宣告一樣。
Using-directive 不會向其出現的宣告區域新增任何名稱(與 using-declaration 不同),因此不會阻止宣告相同的名稱。
Using-指令對於非限定查詢而言是傳遞的:如果一個作用域包含一個提名 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; // 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-directive 被用來提名某個名稱空間後,該名稱空間被擴充套件並添加了額外的成員和/或 using-directive,則這些額外的成員和額外的名稱空間透過 using-directive 可見(與 using-declaration 不同)。
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-directive using namespace std; 會將名稱空間 std
中的每個名稱引入全域性名稱空間(因為全域性名稱空間是包含 std
和任何使用者宣告名稱空間的最近名稱空間),這可能導致不必要的名稱衝突。在標頭檔案的檔案作用域中使用此和其他 using-directive 通常被認為是糟糕的實踐(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++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 101 | C++98 | 如果名稱空間作用域或塊作用域中的函式宣告和透過 using-宣告引入的函式聲明瞭相同的函式(無歧義),則程式格式錯誤。 scope or block scope and a function introduced by a using-declaration declare the same function (no ambiguity) |
允許 |
CWG 373 | C++98 | 查詢只考慮 using-指令運算元中最後一個名稱的名稱空間宣告(這是次優的,因為類不能包含名稱空間) the last name in the operand of a using-directive (which is sub-optimal, because classes cannot contain namespaces) |
查詢限制 適用於所有名稱 using-指令的運算元 |
CWG 460 | C++98 | using-宣告可以命名一個名稱空間 | 已禁止 |
CWG 565 | C++98 | using-宣告不能引入與同一作用域中另一個函式相同的函式,但此限制不適用於函式模板。 identical to another function in the same scope, but the restriction was not applied to function templates |
應用相同的限制 也適用於函式模板 |
CWG 986 | C++98 | using-指令對限定查詢具有傳遞性 | 只對非限定查詢具有傳遞性 |
CWG 987 | C++98 | 巢狀名稱空間中宣告的實體 也是外圍名稱空間的成員 |
巢狀作用域排除在外 |
CWG 1021 | C++98 | 不清楚透過 using-宣告引入名稱空間的實體定義是否被認為是定義在該名稱空間中 is introduced to a namespace via using-declaration is considered to be defined in that namespace |
未在該名稱空間中定義 |
CWG 1838 | C++98 | 外部名稱空間中的非限定定義 可以定義在另一個名稱空間中宣告但未定義的實體,並透過 using 引入。 another namespace and pulled in by a using |
非限定定義 總是指 其名稱空間 |
CWG 2155 | C++98 | CWG issue 1838 的解決方案未應用於類和列舉宣告。 applied to class and enumeration declarations |
已應用 |
[編輯] 另請參閱
命名空間別名 | 建立現有名稱空間的別名 |