名前空間
変種
操作

Using-declaration

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

この 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 のアクセス可能性は無視されます。

そのような派生クラスのオブジェクトを初期化する際に、オーバーロード解決が継承されたコンストラクタのいずれかを選択した場合、コンストラクタが継承された Base サブオブジェクトは継承されたコンストラクタを使用して初期化され、Derived の他のすべての基底とメンバーはデフォルト化されたデフォルトコンストラクタによって初期化されるかのように初期化されます (指定されている場合はデフォルトメンバー初期化子が使用され、それ以外の場合はデフォルト初期化が行われます)。全体の初期化は単一の関数呼び出しとして扱われます。継承されたコンストラクタのパラメーターの初期化は、派生オブジェクトの基底またはメンバーの初期化の前に順序付けられます

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 オブジェクトの一部として初期化されない場合 (つまり、BaseDerived仮想基底クラスであり、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)
}

コンストラクタが Base 型の複数の基底クラスサブオブジェクトから継承された場合、多重継承された非静的メンバー関数と同様に、プログラムは不正です。

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 と同様に、継承されたコンストラクタが Derived のコンストラクタのいずれかのシグネチャと一致する場合、それは Derived で見つかったバージョンによってルックアップから隠蔽されます。Base の継承されたコンストラクタのいずれかが、Derived のコピー/ムーブコンストラクタと一致するシグネチャを持つ場合、それは Derived の暗黙的なコピー/ムーブコンストラクタの生成を妨げません (そして、それは継承されたバージョンを隠蔽します。using operator= と同様です)。

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-specifierunqualified-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 で実装不可能でした。古いコンパイラは依然として以前のセマンティクスを実装している可能性があります。

古いコンストラクタ継承のセマンティクス

using-declaration が定義中のクラスの直接基底のコンストラクタを参照する場合 (例: using Base::Base;)、その基底クラスのコンストラクタは、次の規則に従って継承されます。

1) 候補継承コンストラクタのセットは以下で構成されます。
a) 基底クラスのすべての非テンプレートコンストラクタ (省略記号パラメーターがある場合、それらを省略した後)(C++14 以降)
b) デフォルト引数または省略記号パラメーターを持つ各コンストラクタについて、省略記号を削除し、引数リストの末尾からデフォルト引数を1つずつ省略して形成されるすべてのコンストラクタシグネチャ
c) 基底クラスのすべてのコンストラクタテンプレート (省略記号パラメーターがある場合、それらを省略した後)(C++14 以降)
d) デフォルト引数または省略記号を持つ各コンストラクタテンプレートについて、省略記号を削除し、引数リストの末尾からデフォルト引数を1つずつ省略して形成されるすべてのコンストラクタシグネチャ
2) デフォルトコンストラクタでもコピー/ムーブコンストラクタでもなく、派生クラスのユーザー定義コンストラクタとシグネチャが一致しないすべての候補継承コンストラクタは、派生クラスに暗黙的に宣言されます。デフォルトパラメーターは継承されません。
struct B1
{
    B1(int);
};
 
struct D1 : B1
{
    using B1::B1;
 
    // The set of candidate inherited constructors is 
    // 1. B1(const B1&)
    // 2. B1(B1&&)
    // 3. B1(int)
 
    // D1 has the following constructors:
    // 1. D1() = delete
    // 2. D1(const D1&) 
    // 3. D1(D1&&)
    // 4. D1(int) <- inherited
};
 
struct B2
{
    B2(int = 13, int = 42);
};
 
struct D2 : B2
{
    using B2::B2;
 
    // The set of candidate inherited constructors is
    // 1. B2(const B2&)
    // 2. B2(B2&&)
    // 3. B2(int = 13, int = 42)
    // 4. B2(int = 13)
    // 5. B2()
 
    // D2 has the following constructors:
    // 1. D2()
    // 2. D2(const D2&)
    // 3. D2(D2&&)
    // 4. D2(int, int) <- inherited
    // 5. D2(int) <- inherited
};

継承されたコンストラクタは、空の本体を持ち、すべての引数を基底クラスコンストラクタに転送する単一の nested-name-specifier からなるメンバー初期化子リストを持つユーザー定義コンストラクタと同等です。

対応する基底コンストラクタと同じアクセスを持ちます。ユーザー定義コンストラクタが constexpr コンストラクタ要件を満たしていれば、constexpr となります。対応する基底コンストラクタが削除されているか、デフォルト化されたデフォルトコンストラクタが削除される場合 (コンストラクタが継承されている基底の構築は数えない)、削除されます。継承コンストラクタは明示的にインスタンス化することも、明示的に特殊化することもできません。

2つの using-declarations が同じシグネチャを持つコンストラクタを継承する場合 (2つの直接基底クラスから)、プログラムは不正形式です。

継承コンストラクタテンプレートは明示的にインスタンス化したり、明示的に特殊化したりするべきではありません。

(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 におけるパック展開

[編集] キーワード

using

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 258 C++98 派生クラスの非 const メンバー関数は、
その基底の const メンバー関数をオーバーライドおよび/または隠蔽できる。
オーバーライドと隠蔽には、
cv-修飾子も同じである必要がある。
CWG 1738 C++11 明示的にインスタンス化または明示的に特殊化することが許可されているかどうかが不明確であった。
明示的にインスタンス化または明示的に特殊化
継承コンストラクタテンプレートの特殊化
禁止された
CWG 2504 C++11 仮想基底クラスからの
コンストラクタの継承の動作が不明確であった。
明確化された
P0136R1 C++11 継承コンストラクタ宣言は、
派生クラスに追加のコンストラクタを挿入する。
基底クラスコンストラクタが
名前ルックアップによって見つかる原因となる。
  1. 参照

[編集] 参照

  • C++23標準 (ISO/IEC 14882:2024)
  • 9.9 using 宣言 [namespace.udecl]
  • C++20 standard (ISO/IEC 14882:2020)
  • 9.9 using 宣言 [namespace.udecl]
  • C++17 standard (ISO/IEC 14882:2017)
  • 10.3.3 using 宣言 [namespace.udecl]
  • C++14 standard (ISO/IEC 14882:2014)
  • 7.3.3 using 宣言 [namespace.udecl]
  • C++11 standard (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]
English 日本語 中文(简体) 中文(繁體)