Namensräume
Varianten
Aktionen

Using-Deklaration

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Führt einen Namen, der an anderer Stelle definiert ist, in den deklarierten Bereich ein, in dem diese Using-Deklaration erscheint. Siehe using enum und (seit C++20)using namespace für andere verwandte Deklarationen.

using typename(optional) nested-name-specifier unqualified-id ; (bis C++17)
using declarator-list ; (seit C++17)
typename - Das Schlüsselwort typename kann nach Bedarf verwendet werden, um abhängige Namen aufzulösen, wenn die Using-Deklaration einen Member-Typ aus einer Basisklasse in einer Klassenvorlage einführt.
nested-name-specifier - Eine Sequenz von Namen und Gültigkeitsbereichsauflösungsoperatoren ::, die mit einem Gültigkeitsbereichsauflösungsoperator endet. Ein einzelnes :: bezieht sich auf den globalen Namensraum.
unqualified-id - Ein id-Ausdruck.
declarator-list - Eine durch Kommas getrennte Liste von einem oder mehreren Deklaratoren des Typs typename(optional) nested-name-specifier unqualified-id. Einige oder alle Deklaratoren können von einem Ellipsensymbol ... gefolgt werden, um eine Pack-Expansion anzuzeigen.

Inhalt

[edit] Erklärung

Using-Deklarationen können verwendet werden, um Namensraummitglieder in andere Namensräume und Blockbereiche einzuführen oder Basisklassenmitglieder in abgeleitete Klassendefinitionen einzuführen, oder um Aufzählungswerte in Namensräume, Block- und Klassenbereiche einzuführen(seit C++20).

Eine Using-Deklaration mit mehr als einem Using-Deklarator ist äquivalent zu einer entsprechenden Sequenz von Using-Deklarationen mit einem Using-Deklarator.

(seit C++17)

[edit] Im Namensraum- und Blockbereich

Using-Deklarationen führen ein Mitglied eines anderen Namensraums in den aktuellen Namensraum oder Blockbereich ein.

#include <iostream>
#include <string>
 
using std::string;
 
int main()
{
    string str = "Example";
    using std::cout;
    cout << str;
}

Details finden Sie unter Namensraum.

[edit] In Klassendefinitionen

Eine Using-Deklaration führt ein Mitglied einer Basisklasse in die abgeleitete Klassendefinition ein, z. B. um ein geschütztes Mitglied von `base` als öffentliches Mitglied von `derived` freizulegen. In diesem Fall muss der nested-name-specifier eine Basisklasse der zu definierenden Klasse benennen. Wenn der Name der Name einer überladenen Member-Funktion der Basisklasse ist, werden alle Member-Funktionen der Basisklasse mit diesem Namen eingeführt. Wenn die abgeleitete Klasse bereits ein Mitglied mit demselben Namen, derselben Parameterliste und denselben Qualifizierungen hat, verdeckt das Mitglied der abgeleiteten Klasse das aus der Basisklasse eingeführte Mitglied (ohne Konflikt mit ihm).

#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()
}

Ausgabe

D::f
D::f
----------
D::g
B::g
----------
B::h
D::h

Erbende Konstruktoren

Wenn sich die *Using-Deklaration* auf einen Konstruktor einer direkten Basis der zu definierenden Klasse bezieht (z. B. using Base::Base;), werden alle Konstruktoren dieser Basis (unter Nichtbeachtung des Member-Zugriffs) bei der Initialisierung der abgeleiteten Klasse für die Überladungsauflösung sichtbar gemacht.

Wenn die Überladungsauflösung einen geerbten Konstruktor auswählt, ist er zugänglich, wenn er zugänglich wäre, wenn er zum Konstruieren eines Objekts der entsprechenden Basisklasse verwendet würde: Die Zugänglichkeit der Using-Deklaration, die ihn eingeführt hat, wird ignoriert.

Wenn die Überladungsauflösung einen der geerbten Konstruktoren bei der Initialisierung eines Objekts einer solchen abgeleiteten Klasse auswählt, wird das `Base`-Teilobjekt, von dem der Konstruktor geerbt wurde, mit dem geerbten Konstruktor initialisiert, und alle anderen Basen und Member von `Derived` werden initialisiert, als ob sie durch den standardmäßig deklarierten Standardkonstruktor initialisiert würden (standardmäßige Member-Initialisierer werden verwendet, falls vorhanden, andernfalls erfolgt eine Standardinitialisierung). Die gesamte Initialisierung wird als ein einziger Funktionsaufruf behandelt: Die Initialisierung der Parameter des geerbten Konstruktors erfolgt sequenziell vor der Initialisierung von Basen oder Membern des abgeleiteten Objekts.

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

Wenn das `Base`-Basisklassen-Teilobjekt nicht als Teil des `Derived`-Objekts initialisiert werden soll (d. h. `Base` ist eine virtuelle Basisklasse von `Derived` und das `Derived`-Objekt ist nicht das am weitesten abgeleitete Objekt), wird die Ausführung des geerbten Konstruktors, einschließlich der Auswertung von Argumenten, weggelassen.

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)
}

Wenn der Konstruktor von mehreren Basisklassen-Teilobjekten vom Typ `Base` geerbt wurde, ist das Programm fehlerhaft, ähnlich wie bei mehrfach geerbten Nicht-Static-Member-Funktionen.

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

Wie bei Using-Deklarationen für alle anderen Nicht-Static-Member-Funktionen gilt: Wenn ein geerbter Konstruktor der Signatur eines Konstruktors von `Derived` entspricht, wird er durch die in `Derived` gefundene Version von der Suche verdeckt. Wenn einer der geerbten Konstruktoren von `Base` zufällig die Signatur eines Kopier-/Verschiebekonstruktors von `Derived` hat, verhindert dies nicht die implizite Generierung des Kopier-/Verschiebekonstruktors von `Derived` (der dann die geerbte Version verdeckt, ähnlich wie bei `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)

Innerhalb einer Vorlagenklasse, wenn sich eine Using-Deklaration auf einen abhängigen Namen bezieht, wird sie als Konstruktor betrachtet, wenn der nested-name-specifier einen Endnamen hat, der mit dem unqualified-id übereinstimmt.

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<>
};
(seit C++11)


Geltungsbereichsbezogene Aufzählungen einführen

Zusätzlich zu Mitgliedern anderer Namensräume und Mitgliedern von Basisklassen kann eine Using-Deklaration auch Aufzählungswerte von Aufzählungen in Namensraum-, Block- und Klassenbereiche einführen.

Eine Using-Deklaration kann auch mit unScope-Aufzählungen verwendet werden.

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
(seit C++20)

[edit] Anmerkungen

Nur der explizit in der Using-Deklaration erwähnte Name wird in den Deklarationsbereich übertragen: Insbesondere werden Aufzählungswerte nicht übertragen, wenn der Aufzählungstypsname per Using-Deklaration eingeführt wird.

Eine Using-Deklaration kann sich nicht auf einen Namensraum, auf einen Geltungsbereichs-Aufzählungswert(bis C++20), auf einen Destruktor einer Basisklasse oder auf eine Spezialisierung einer Member-Vorlage für eine benutzerdefinierte Konvertierungsfunktion beziehen.

Eine Using-Deklaration kann kein Member-Vorlagen-Spezialisierung benennen (ein template-id ist durch die Grammatik nicht erlaubt).

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>(); }
};

Eine Using-Deklaration kann auch nicht verwendet werden, um den Namen einer abhängigen Member-Vorlage als *template-name* einzuführen (der `template`-Disambiguator für abhängige Namen ist nicht erlaubt).

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
    }   
};

Wenn eine Using-Deklaration den Zuweisungsoperator der Basisklasse in die abgeleitete Klasse bringt, dessen Signatur mit dem Kopier- oder Zuweisungsoperator der abgeleiteten Klasse übereinstimmt, wird dieser Operator durch den implizit deklarierten Kopier-/Zuweisungsoperator der abgeleiteten Klasse verdeckt. Dasselbe gilt für eine Using-Deklaration, die einen Basisklassen-Konstruktor erbt, der zufällig mit dem Kopier-/Konstruktor der abgeleiteten Klasse übereinstimmt(seit C++11).

Die Semantik des Erbens von Konstruktoren wurde durch ein Defect Report gegen C++11 rückwirkend geändert. Zuvor führte eine erbende Konstruktordeklaration dazu, dass eine Reihe von synthetisierten Konstruktordeklarationen in die abgeleitete Klasse injiziert wurden, was zu redundanten Argumentkopien/-verschiebungen führte, problematische Wechselwirkungen mit einigen Formen von SFINAE hatte und in einigen Fällen auf wichtigen ABIs nicht implementierbar sein konnte. Ältere Compiler können noch die vorherige Semantik implementieren.

Alte Semantik für erbende Konstruktoren

Wenn sich die *Using-Deklaration* auf einen Konstruktor einer direkten Basis der zu definierenden Klasse bezieht (z. B. using Base::Base;), werden Konstruktoren dieser Basisklasse nach folgenden Regeln geerbt:

1) Eine Menge von *Kandidaten für erbende Konstruktoren* besteht aus:
a) Alle nicht-Vorlagen-Konstruktoren der Basisklasse (nachdem Ellipsenparameter, falls vorhanden, weggelassen wurden)(seit C++14)
b) Für jeden Konstruktor mit Standardargumenten oder dem Ellipsenparameter, alle Konstruktorsignaturen, die durch Weglassen der Ellipse und sukzessives Weglassen von Standardargumenten am Ende der Argumentlisten gebildet werden.
c) Alle Konstruktor-Vorlagen der Basisklasse (nachdem Ellipsenparameter, falls vorhanden, weggelassen wurden)(seit C++14)
d) Für jede Konstruktor-Vorlage mit Standardargumenten oder Ellipse, alle Konstruktorsignaturen, die durch Weglassen der Ellipse und sukzessives Weglassen von Standardargumenten am Ende der Argumentlisten gebildet werden.
2) Alle Kandidaten für geerbte Konstruktoren, die nicht der Standardkonstruktor oder der Kopier-/Zuweisungskonstruktor sind und deren Signaturen nicht mit benutzerdefinierten Konstruktoren in der abgeleiteten Klasse übereinstimmen, werden implizit in der abgeleiteten Klasse deklariert. Die Standardparameter werden nicht geerbt.
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
};

Die geerbten Konstruktoren sind äquivalent zu benutzerdefinierten Konstruktoren mit leerem Körper und einer Member-Initialisierungsliste, die aus einem einzigen nested-name-specifier besteht, der alle seine Argumente an den Basisklassen-Konstruktor weiterleitet.

Sie hat denselben Zugriff wie der entsprechende Basisklassen-Konstruktor. Sie ist `constexpr`, wenn der benutzerdefinierte Konstruktor die Anforderungen für `constexpr`-Konstruktoren erfüllt hätte. Sie ist gelöscht, wenn der entsprechende Basisklassen-Konstruktor gelöscht ist oder wenn ein standardmäßig deklarierter Standardkonstruktor gelöscht wäre (außer dass die Konstruktion der Basis, deren Konstruktor geerbt wird, nicht zählt). Ein erbender Konstruktor kann nicht explizit instanziiert oder explizit spezialisiert werden.

Wenn zwei Using-Deklarationen den Konstruktor mit derselben Signatur (von zwei direkten Basisklassen) erben, ist das Programm fehlerhaft.

Eine erbende Konstruktor-Vorlage sollte nicht explizit instanziiert oder explizit spezialisiert werden.

(seit C++11)

Pack-Expansionen in Using-Deklarationen ermöglichen die Bildung einer Klasse, die überladene Member von variadischen Basen ohne Rekursion freilegt.

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;} };
}
(seit C++17)
Feature-Testmakro Wert Std Feature
__cpp_inheriting_constructors 200802L (C++11) Erbende Konstruktoren
201511L (C++17)
(DR11)
Umformulierung von erbenden Konstruktoren
__cpp_variadic_using 201611L (C++17) Pack-Expansionen in `using`-Deklarationen

[edit] Schlüsselwörter

using

[edit] Defect Reports

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 258 C++98 Eine Nicht-`const`-Member-Funktion einer abgeleiteten Klasse kann
eine `const`-Member-Funktion ihrer Basis überschreiben und/oder verdecken
Überschreiben und Verdecken erfordern außerdem
gleiche CV-Qualifizierungen.
CWG 1738 C++11 Es war unklar, ob es erlaubt ist,
explizit zu instanziieren oder zu spezialisieren
Spezialisierungen von erbenden Konstruktor-Vorlagen.
verboten
CWG 2504 C++11 Das Verhalten von erbenden Konstruktoren
von virtuellen Basisklassen war unklar.
wurde klargestellt
P0136R1 C++11 Die Deklaration eines erbenden Konstruktors injiziert
zusätzliche Konstruktoren in die abgeleitete Klasse
verursacht Basisklassen-Konstruktoren,
die durch Namenssuche gefunden werden.
  1. Referenzen

[edit] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 9.9 Die `using`-Deklaration [namespace.udecl]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 9.9 Die `using`-Deklaration [namespace.udecl]
  • C++17 Standard (ISO/IEC 14882:2017)
  • 10.3.3 Die `using`-Deklaration [namespace.udecl]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 7.3.3 Die `using`-Deklaration [namespace.udecl]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 7.3.3 Die `using`-Deklaration [namespace.udecl]
  • C++03-Standard (ISO/IEC 14882:2003)
  • 7.3.3 Die `using`-Deklaration [namespace.udecl]
  • C++98 Standard (ISO/IEC 14882:1998)
  • 7.3.3 Die `using`-Deklaration [namespace.udecl]