using 宣告
將定義在其他地方的名字引入到此 using 宣告出現的宣告區域中。參見 using enum 以及 (C++20 起)using namespace 以瞭解其他相關的宣告。
using typename (可選) 巢狀名說明符 非限定 ID ; |
(C++17 前) | ||||||||
using 宣告符列表 ; |
(C++17 起) | ||||||||
typename
|
- | 當 using 宣告從基類將成員型別引入到類模板中時,可根據需要使用關鍵字 typename 來解析依賴名。 |
巢狀名說明符 | - | 一串名字和作用域解析運算子 :: ,以作用域解析運算子結尾。單個 :: 指全域性名稱空間。 |
非限定 ID | - | 一個id-表示式 |
宣告符列表 | - | 由逗號分隔的一個或多個 typename (可選) 巢狀名說明符 非限定 ID 宣告符列表。部分或全部宣告符後可以跟省略號 ... 以指示包擴充套件。 |
目錄 |
[編輯] 解釋
using 宣告可用於將名稱空間成員引入其他名稱空間和塊作用域,或將基類成員引入派生類定義中,或將列舉器引入名稱空間、塊和類作用域(C++20 起)。
具有多個 using 宣告符的 using 宣告等價於一個由相應的一個 using 宣告符組成的 using 宣告序列。 |
(C++17 起) |
[編輯] 在名稱空間和塊作用域中
using 宣告將另一個名稱空間的成員引入當前名稱空間或塊作用域。
#include <iostream> #include <string> using std::string; int main() { string str = "Example"; using std::cout; cout << str; }
詳見名稱空間。
[編輯] 在類定義中
Using 宣告將基類的成員引入派生類定義中,例如將基類的受保護成員公開為派生類的公共成員。在這種情況下,巢狀名說明符必須命名正在定義的類的基類。如果該名稱是基類中過載成員函式的名稱,則所有具有該名稱的基類成員函式都會被引入。如果派生類已經擁有具有相同名稱、引數列表和限定符的成員,則派生類成員會隱藏或覆蓋(不與)從基類引入的成員。
#include <iostream> struct B { virtual void f(int) { std::cout << "B::f\n"; } void g(char) { std::cout << "B::g\n"; } void h(int) { std::cout << "B::h\n"; } protected: int m; // B::m is protected typedef int value_type; }; struct D : B { using B::m; // D::m is public using B::value_type; // D::value_type is public using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) overrides B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // both g(int) and g(char) are visible using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) hides B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // Error: B::m is protected d.m = 1; // protected B::m is accessible as public D::m b.f(1); // calls derived f() d.f(1); // calls derived f() std::cout << "----------\n"; d.g(1); // calls derived g(int) d.g('a'); // calls base g(char), exposed via using B::g; std::cout << "----------\n"; b.h(1); // calls base h() d.h(1); // calls derived h() }
輸出
D::f D::f ---------- D::g B::g ---------- B::h D::h
繼承建構函式如果 using-declaration 指的是正在定義的類的直接基類的建構函式(例如 using Base::Base;),那麼當初始化派生類時,該基類的所有建構函式(忽略成員訪問)都將對過載決議可見。 如果過載決議選擇了一個繼承的建構函式,那麼當它用於構造相應基類的物件時,它是可訪問的:引入它的 using 宣告的可訪問性被忽略。 如果在初始化這樣的派生類物件時,過載決議選擇了一個繼承的建構函式,那麼繼承該建構函式的 struct B1 { B1(int, ...) {} }; struct B2 { B2(double) {} }; int get(); struct D1 : B1 { using B1::B1; // inherits B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4), // then d.x is default-initialized (no initialization is performed), // then d.y is initialized by calling get() D1 e; // Error: D1 has no default constructor } struct D2 : B2 { using B2::B2; // inherits B2(double) B1 b; }; D2 f(1.0); // error: B1 has no default constructor struct W { W(int); }; struct X : virtual W { using W::W; // inherits W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: initialization of Y does not invoke default constructor of X 如果 `Base` 基類子物件不作為 `Derived` 物件的一部分進行初始化(即,`Base` 是 `Derived` 的虛基類,且 `Derived` 物件不是最派生物件),則會省略對繼承建構函式的呼叫,包括任何引數的求值。 struct V { V() = default; V(int); }; struct Q { Q(); }; struct A : virtual V, Q { using V::V; A() = delete; }; int bar() { return 42; } struct B : A { B() : A(bar()) {} // OK }; struct C : B {}; void foo() { C c; // “bar” is not invoked, because the V subobject // is not initialized as part of B // (the V subobject is initialized as part of C, // because “c” is the most derived object) } 如果建構函式是從型別為 struct A { A(int); }; struct B : A { using A::A; }; struct C1 : B { using B::B; }; struct C2 : B { using B::B; }; struct D1 : C1, C2 { using C1::C1; using C2::C2; }; D1 d1(0); // ill-formed: constructor inherited from different B base subobjects struct V1 : virtual B { using B::B; }; struct V2 : virtual B { using B::B; }; struct D2 : V1, V2 { using V1::V1; using V2::V2; }; D2 d2(0); // OK: there is only one B subobject. // This initializes the virtual B base class, // which initializes the A base class // then initializes the V1 and V2 base classes // as if by a defaulted default constructor 與任何其他非靜態成員函式的 using 宣告一樣,如果繼承的建構函式與 struct B1 { B1(int); }; struct B2 { B2(int); }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK: D2::D2(int) hides both B1::B1(int) and B2::B2(int) }; D2 d2(0); // calls D2::D2(int) 在一個模板類中,如果一個 using 宣告引用了一個依賴名,則如果 巢狀名說明符 的末尾名稱與 非限定 ID 相同,則它被認為是命名一個建構函式。 template<class T> struct A : T { using T::T; // OK, inherits constructors of T }; template<class T, class U> struct B : T, A<U> { using A<U>::A; // OK, inherits constructors of A<U> using T::A; // does not inherit constructor of T // even though T may be a specialization of A<> }; |
(C++11 起) |
引入有作用域的列舉器除了其他名稱空間的成員和基類的成員,using 宣告還可以將列舉的列舉器引入名稱空間、塊和類作用域。 using 宣告也可以與無作用域列舉器一起使用。 enum class button { up, down }; struct S { using button::up; button b = up; // OK }; using button::down; constexpr button non_up = down; // OK constexpr auto get_button(bool is_up) { using button::up, button::down; return is_up ? up : down; // OK } enum unscoped { val }; using unscoped::val; // OK, though needless |
(C++20 起) |
[編輯] 注意
只有在 using 宣告中明確提及的名稱才會被轉移到宣告性作用域:特別地,當列舉型別名稱被 using 宣告時,列舉器不會被轉移。
using 宣告不能引用名稱空間、有作用域的列舉器(C++20 前)、基類的解構函式或使用者定義轉換函式的成員模板特化。
using 宣告不能命名成員模板特化(語法不允許 template-id)
struct B { template<class T> void f(); }; struct D : B { using B::f; // OK: names a template // using B::f<int>; // Error: names a template specialization void g() { f<int>(); } };
using 宣告也不能用於將依賴成員模板的名稱作為 template-name 引入(不允許對依賴名使用 template
消歧符)。
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // Error: disambiguator not allowed using B<Y>::f; // compiles, but f is not a template-name void g() { // f<int>(0); // Error: f is not known to be a template name, // so < does not start a template argument list f(0); // OK } };
如果一個 using 宣告將基類賦值運算子引入派生類,且其簽名恰好與派生類的複製賦值或移動賦值運算子匹配,則該運算子會被派生類的隱式宣告的複製/移動賦值運算子隱藏。同樣適用於繼承基類建構函式且恰好與派生類複製/移動建構函式匹配的 using 宣告(C++11 起)。
繼承建構函式的語義被針對 C++11 的缺陷報告追溯修改。此前,繼承建構函式宣告會在派生類中注入一組合成的建構函式宣告,這導致冗餘的引數複製/移動,與某些 SFINAE 形式存在問題,並且在某些情況下在主要 ABI 上無法實現。舊編譯器可能仍實現以前的語義。
|
(C++11 起) |
using 宣告中的包擴充套件使得無需遞迴即可形成一個暴露可變基類過載成員的類。 template<typename... Ts> struct Overloader : Ts... { using Ts::operator()...; // exposes operator() from every base }; template<typename... T> Overloader(T...) -> Overloader<T...>; // C++17 deduction guide, not needed in C++20 int main() { auto o = Overloader{ [] (auto const& a) {std::cout << a;}, [] (float f) {std::cout << std::setprecision(3) << f;} }; } |
(C++17 起) |
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_inheriting_constructors |
200802L |
(C++11) | 繼承建構函式 |
201511L |
(C++17) (DR11) |
重新措辭繼承建構函式 | |
__cpp_variadic_using |
201611L |
(C++17) | using 宣告中的包擴充套件 |
[編輯] 關鍵詞
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 258 | C++98 | 派生類的非 const 成員函式可以 覆蓋和/或隱藏其基類的 const 成員函式 |
覆蓋和隱藏還需要 cv-限定符相同 |
CWG 1738 | C++11 | 不清楚是否允許 顯式例項化或顯式特化 繼承建構函式模板的特化 |
已禁止 |
CWG 2504 | C++11 | 繼承建構函式的行為 來自虛基類不明確 |
已明確 |
P0136R1 | C++11 | 繼承建構函式宣告注入 派生類中的額外建構函式 |
導致基類建構函式 透過名稱查詢找到 |
- 引用
[編輯] 參考
- C++23 標準 (ISO/IEC 14882:2024)
- 9.9 using 宣告 [namespace.udecl]
- C++20 標準 (ISO/IEC 14882:2020)
- 9.9 using 宣告 [namespace.udecl]
- C++17 標準 (ISO/IEC 14882:2017)
- 10.3.3 using 宣告 [namespace.udecl]
- C++14 標準 (ISO/IEC 14882:2014)
- 7.3.3 using 宣告 [namespace.udecl]
- C++11 標準 (ISO/IEC 14882:2011)
- 7.3.3 using 宣告 [namespace.udecl]
- C++03 標準 (ISO/IEC 14882:2003)
- 7.3.3 using 宣告 [namespace.udecl]
- C++98 標準 (ISO/IEC 14882:1998)
- 7.3.3 using 宣告 [namespace.udecl]