Using-declaration
他の場所で定義されている名前を、このusing宣言が現れる宣言領域に導入します。関連する他の宣言については、 using enum および (C++20以降) using namespace を参照してください。
using
typename
(オプション)
nested-name-specifier
unqualified-id
;
|
(C++17まで) | ||||||||
using
declarator-list
;
|
(C++17以降) | ||||||||
typename
|
- | キーワード typename は、using宣言が基底クラスからクラステンプレートにメンバ型を導入する際に 依存名 を解決するために必要に応じて使用可能 |
| nested-name-specifier | - |
名前とスコープ解決演算子
::
のシーケンスで、スコープ解決演算子で終わる。単一の
::
はグローバル名前空間を参照
|
| unqualified-id | - | 識別子式 |
| declarator-list | - |
typename
(オプション)
nested-name-specifier
unqualified-id
形式の1つ以上の宣言子のカンマ区切りリスト。宣言子の一部または全ては
パック展開
を示す省略記号
...
が続く場合あり
|
目次 |
説明
using宣言は、名前空間メンバーを他の名前空間やブロックスコープに導入するため、または基底クラスのメンバーを派生クラス定義に導入するために使用できます 、または enumerators を名前空間、ブロック、クラススコープに導入するために使用できます (C++20以降) 。
|
複数のusing宣言子を持つusing宣言は、1つのusing宣言子を持つ対応する一連のusing宣言と等価である。 |
(since C++17) |
名前空間スコープとブロックスコープ内
Using-declarations は、他の名前空間のメンバーを現在の名前空間またはブロックスコープに導入します。
#include <iostream> #include <string> using std::string; int main() { string str = "Example"; using std::cout; cout << str; }
詳細については namespace を参照してください。
クラス定義内
using宣言は、基底クラスのメンバーを派生クラスの定義に導入します。例えば、基底クラスの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 は protected typedef int value_type; }; struct D : B { using B::m; // D::m は public using B::value_type; // D::value_type は public using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) は B::f(int) をオーバーライド using B::g; void g(int) { std::cout << "D::g\n"; } // g(int) と g(char) の両方が可視 using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) は B::h(int) を隠蔽 }; int main() { D d; B& b = d; // b.m = 2; // エラー: B::m は protected d.m = 1; // protected B::m は public D::m としてアクセス可能 b.f(1); // 派生クラスの f() を呼び出し d.f(1); // 派生クラスの f() を呼び出し std::cout << "----------\n"; d.g(1); // 派生クラスの g(int) を呼び出し d.g('a'); // 基底クラスの g(char) を呼び出し(using B::g; で公開) std::cout << "----------\n"; b.h(1); // 基底クラスの h() を呼び出し d.h(1); // 派生クラスの 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; // B1(int, ...) を継承 int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 は B1(2, 3, 4) の呼び出しで初期化され、 // 次に d.x はデフォルト初期化され(初期化は実行されない)、 // 次に d.y は get() の呼び出しで初期化される D1 e; // エラー: D1 はデフォルトコンストラクタを持たない } struct D2 : B2 { using B2::B2; // B2(double) を継承 B1 b; }; D2 f(1.0); // エラー: B1 はデフォルトコンストラクタを持たない struct W { W(int); }; struct X : virtual W { using W::W; // W(int)を継承 X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: Yの初期化は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」は呼び出されない。なぜならVの部分オブジェクトは // Bの一部として初期化されないため // (Vの部分オブジェクトはCの一部として初期化される。 // なぜなら「c」が最も派生したオブジェクトであるため) }
コンストラクタが型
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); // 不適格: 異なるB基底部分オブジェクトから継承されたコンストラクタ 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: B部分オブジェクトは1つだけ存在する // これは仮想B基底クラスを初期化し、 // A基底クラスを初期化し、 // その後V1とV2基底クラスを // デフォルト化されたデフォルトコンストラクタによって初期化する
他の非静的メンバ関数に対する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) は B1::B1(int) と B2::B2(int) の両方を隠蔽する }; D2 d2(0); // D2::D2(int) を呼び出す テンプレート化された クラス内 では、using宣言が 依存名 を参照する場合、 修飾名指定子 の終端名が 非修飾ID と同じであるとき、コンストラクタを指定していると見なされます。 template<class T> struct A : T { using T::T; // OK、Tのコンストラクタを継承 }; template<class T, class U> struct B : T, A<U> { using A<U>::A; // OK、A<U>のコンストラクタを継承 using T::A; // Tのコンストラクタを継承しない // たとえTが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: テンプレートを指定 // using B::f<int>; // エラー: テンプレート特殊化を指定 void g() { f<int>(); } };
using宣言は依存メンバテンプレートの名前を
template-name
として導入するためにも使用できません
(
dependent names
に対する
template
曖昧性除去子は許可されていません)。
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // エラー: 曖昧性除去子は許可されていません using B<Y>::f; // コンパイルは成功しますが、fはテンプレート名ではありません void g() { // f<int>(0); // エラー: fがテンプレート名であることが認識されないため、 // <はテンプレート引数リストの開始として機能しません 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++規格に対して遡及的に適用されました。
| 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 標準 (ISO/IEC 14882:2020):
-
-
9.9
using宣言 [namespace.udecl]
-
9.9
- C++17 標準 (ISO/IEC 14882:2017):
-
-
10.3.3
using宣言 [namespace.udecl]
-
10.3.3
- C++14標準 (ISO/IEC 14882:2014):
-
-
7.3.3
using宣言 [namespace.udecl]
-
7.3.3
- C++11標準 (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