Namespaces
Variants

Using-declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

他の場所で定義されている名前を、この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宣言のアクセシビリティは無視されます。

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

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のデフォルトコンストラクタを呼び出さない

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」は呼び出されない。なぜならVの部分オブジェクトは
         // Bの一部として初期化されないため
         // (Vの部分オブジェクトはCの一部として初期化される。
         //  なぜなら「c」が最も派生したオブジェクトであるため)
}

コンストラクタが型 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); // 不適格: 異なる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宣言と同様に、継承されたコンストラクタが Derived のコンストラクタのシグネチャと一致する場合、 Derived で見つかったバージョンによってルックアップから隠蔽されます。 Base の継承されたコンストラクタの1つが 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) は 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では実装不可能な場合がありました。古いコンパイラは以前のセマンティクスを実装している場合があります。

旧継承コンストラクタセマンティクス

using宣言 が定義中のクラスの直接基底クラスのコンストラクタを参照する場合(例: 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
};

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

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

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

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

(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 宣言で使用

キーワード

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]
  • 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]