Using-declaration
この using-declaration が出現する宣言領域に、他の場所で定義されている名前を導入します。using enum および (C++20 以降)using namespace は、関連する他の宣言を参照してください。
using typename(オプション) nested-name-specifier unqualified-id ; |
(C++17まで) | ||||||||
using declarator-list ; |
(C++17以降) | ||||||||
typename
|
- | using-declaration が基底クラスからクラステンプレートにメンバー型を導入する場合、typename キーワードは依存名を解決するために必要に応じて使用できます。 |
| nested-name-specifier | - | 名前とスコープ解決演算子 :: のシーケンスで、スコープ解決演算子で終わります。単一の :: はグローバル名前空間を参照します。 |
| unqualified-id | - | id-expression |
| declarator-list | - | typename(オプション) nested-name-specifier unqualified-id の 1 つ以上の宣言子のコンマ区切りリスト。宣言子の一部またはすべてに省略記号 ... が続き、パック展開を示すことができます。 |
目次 |
[編集] 説明
using-declaration は、名前空間メンバーを他の名前空間やブロックスコープに導入したり、基底クラスメンバーを派生クラス定義に導入したりできます。また、列挙子を名前空間、ブロック、およびクラススコープに導入することもできます(C++20 以降)。
|
複数の using-declarator を持つ using-declaration は、対応する 1 つの using-declarator を持つ using-declaration のシーケンスと等価です。 |
(C++17以降) |
[編集] 名前空間およびブロックスコープ内
Using-declarations は、他の名前空間のメンバーを現在の名前空間またはブロックスコープに導入します。
#include <iostream> #include <string> using std::string; int main() { string str = "Example"; using std::cout; cout << str; }
詳細は名前空間を参照してください。
[編集] クラス定義内
using-declaration は、基底クラスのメンバーを派生クラス定義に導入します。例えば、基底の protected メンバーを派生クラスの public メンバーとして公開する場合などです。この場合、nested-name-specifier は定義中のクラスの基底クラスを指す必要があります。名前が基底クラスのオーバーロードされたメンバー関数の名前である場合、その名前を持つすべての基底クラスメンバー関数が導入されます。派生クラスに同じ名前、パラメーターリスト、および修飾子を持つメンバーが既に存在する場合、派生クラスメンバーは基底クラスから導入されたメンバーを隠蔽またはオーバーライドします (競合しません)。
#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-declaration のアクセス可能性は無視されます。 そのような派生クラスのオブジェクトを初期化する際に、オーバーロード解決が継承されたコンストラクタのいずれかを選択した場合、コンストラクタが継承された 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
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-declaration と同様に、継承されたコンストラクタが 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-declaration が依存名を参照する場合、nested-name-specifier が unqualified-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-declaration は列挙型の列挙子を名前空間、ブロック、およびクラススコープに導入することもできます。 using-declaration は、スコープなし列挙子でも使用できます。 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-declaration で明示的に言及された名前のみが宣言スコープに転送されます。特に、列挙型名が using-declaration された場合でも、列挙子は転送されません。
using-declaration は、名前空間、スコープ付き列挙子(C++20 まで)、基底クラスのデストラクタ、またはユーザー定義変換関数のメンバーテンプレートの特殊化を参照することはできません。
using-declaration はメンバーテンプレートの特殊化を指すことはできません (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-declaration は、依存メンバーテンプレートの名前をテンプレート名として導入することもできません (依存名に対する 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-declaration が基底クラスの代入演算子を派生クラスに導入し、そのシグネチャが派生クラスのコピー代入演算子またはムーブ代入演算子と偶然一致する場合、その演算子は派生クラスの暗黙的に宣言されたコピー/ムーブ代入演算子によって隠蔽されます。同様に、派生クラスのコピー/ムーブコンストラクタと偶然一致する基底クラスコンストラクタを継承する using-declaration にも適用されます(C++11 以降)。
|
コンストラクタの継承のセマンティクスは、C++11 に対する欠陥報告によって遡及的に変更されました。以前は、コンストラクタの継承宣言により、合成されたコンストラクタ宣言のセットが派生クラスに注入され、冗長な引数のコピー/ムーブが発生し、一部の SFINAE 形式との問題のある相互作用があり、場合によっては主要な ABI で実装不可能でした。古いコンパイラは依然として以前のセマンティクスを実装している可能性があります。
|
(C++11以降) |
|
using-declaration 内のパック展開により、再帰なしで可変個基底のオーバーロードされたメンバーを公開するクラスを形成できます。 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-declarations におけるパック展開 |
[編集] キーワード
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| 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]
- 9.9
- C++20 standard (ISO/IEC 14882:2020)
- 9.9
using宣言 [namespace.udecl]
- 9.9
- C++17 standard (ISO/IEC 14882:2017)
- 10.3.3
using宣言 [namespace.udecl]
- 10.3.3
- C++14 standard (ISO/IEC 14882:2014)
- 7.3.3
using宣言 [namespace.udecl]
- 7.3.3
- C++11 standard (ISO/IEC 14882:2011)
- 7.3.3
using宣言 [namespace.udecl]
- 7.3.3
- C++03 標準 (ISO/IEC 14882:2003)
- 7.3.3
using宣言 [namespace.udecl]
- 7.3.3
- C++98 標準 (ISO/IEC 14882:1998)
- 7.3.3
using宣言 [namespace.udecl]
- 7.3.3