Using-declaration
Führt einen Namen, der an anderer Stelle definiert ist, in den deklarativen Bereich ein, in dem diese using-Deklaration erscheint. Siehe using enum und (since 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 eine Klassen-Template einführt |
| nested-name-specifier | - |
eine Folge von Namen und Bereichsauflösungsoperatoren
::
, die mit einem Bereichsauflösungsoperator endet. Ein einzelnes
::
bezieht sich auf den globalen Namensraum.
|
| unqualified-id | - | ein Id-Ausdruck |
| declarator-list | - |
kommagetrennte Liste von einem oder mehreren Deklaratoren der Form
typename
(optional)
nested-name-specifier
unqualified-id
. Einige oder alle Deklaratoren können mit einer Auslassungspunktefolge
...
versehen sein, um eine
Paketentfaltung
anzuzeigen
|
Inhaltsverzeichnis |
Erklärung
Using-Deklarationen können verwendet werden, um Namespace-Mitglieder in andere Namespaces und Blockbereiche einzuführen, oder um Basisklassenmitglieder in abgeleitete Klassendefinitionen einzuführen , oder um Enumeratoren in Namespaces, Block- und Klassenbereiche einzuführen (since C++20) .
|
Eine using-Deklaration mit mehr als einem using-Deklarator entspricht einer entsprechenden Folge von using-Deklarationen mit jeweils einem using-Deklarator. |
(since C++17) |
Im Namespace- und Blockbereich
Using-declarations führen ein Mitglied eines anderen Namespace in den aktuellen Namespace oder Blockbereich ein.
#include <iostream> #include <string> using std::string; int main() { string str = "Beispiel"; using std::cout; cout << str; }
Siehe namespace für Details.
In der Klassendefinition
Die using-Deklaration führt ein Mitglied einer Basisklasse in die abgeleitete Klassendefinition ein, beispielsweise um ein geschütztes Mitglied der Basis als öffentliches Mitglied der abgeleiteten Klasse verfügbar zu machen. In diesem Fall muss der nested-name-specifier eine Basisklasse der gerade definierten Klasse benennen. Wenn der Name der Name einer überladenen Memberfunktion der Basisklasse ist, werden alle Basisklassen-Memberfunktionen mit diesem Namen eingeführt. Wenn die abgeleitete Klasse bereits ein Mitglied mit demselben Namen, derselben Parameterliste und denselben Qualifikationen hat, verdeckt oder überschreibt das abgeleitete Klassenmitglied das aus der Basisklasse eingeführte Mitglied (steht nicht im Konflikt damit).
#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 ist protected typedef int value_type; }; struct D : B { using B::m; // D::m ist public using B::value_type; // D::value_type ist public using B::f; void f(int) override { std::cout << "D::f\n"; } // D::f(int) überschreibt B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // sowohl g(int) als auch g(char) sind sichtbar using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) versteckt B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // Fehler: B::m ist protected d.m = 1; // protected B::m ist als public D::m zugreifbar b.f(1); // ruft abgeleitetes f() auf d.f(1); // ruft abgeleitetes f() auf std::cout << "----------\n"; d.g(1); // ruft abgeleitetes g(int) auf d.g('a'); // ruft Basis g(char) auf, verfügbar gemacht via using B::g; std::cout << "----------\n"; b.h(1); // ruft Basis h() auf d.h(1); // ruft abgeleitetes h() auf }
Ausgabe:
D::f D::f ---------- D::g B::g ---------- B::h D::h
Vererbende KonstruktorenWenn die using-Deklaration auf einen Konstruktor einer direkten Basis der definierten Klasse verweist (z.B. using Base :: Base ; ), werden alle Konstruktoren dieser Basis (unter Ignorierung des Member-Zugriffs) für die Überlagerungsauflösung bei der Initialisierung der abgeleiteten Klasse sichtbar gemacht. Wenn die Überladungsauflösung einen geerbten Konstruktor auswählt, ist er zugreifbar, wenn er zugreifbar wäre, wenn er zum Konstruieren eines Objekts der entsprechenden Basisklasse verwendet würde: Die Zugreifbarkeit der using-Deklaration, die ihn eingeführt hat, wird ignoriert.
Wenn die Überladungsauflösung beim Initialisieren eines Objekts einer solchen abgeleiteten Klasse einen der geerbten Konstruktoren auswählt, dann wird das
struct B1 { B1(int, ...) {} }; struct B2 { B2(double) {} }; int get(); struct D1 : B1 { using B1::B1; // erbt B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 wird durch Aufruf von B1(2, 3, 4) initialisiert, // dann wird d.x standardinitialisiert (keine Initialisierung erfolgt), // dann wird d.y durch Aufruf von get() initialisiert D1 e; // Fehler: D1 hat keinen Standardkonstruktor } struct D2 : B2 { using B2::B2; // erbt B2(double) B1 b; }; D2 f(1.0); // Fehler: B1 hat keinen Standardkonstruktor struct W { W(int); }; struct X : virtual W { using W::W; // erbt W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK: Initialisierung von Y ruft nicht den Standardkonstruktor von X auf
Wenn das
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" wird nicht aufgerufen, da das V-Subobjekt // nicht als Teil von B initialisiert wird // (das V-Subobjekt wird als Teil von C initialisiert, // da "c" das am stärksten abgeleitete Objekt ist) }
Wenn der Konstruktor von mehreren Basisklassen-Subobjekten des Typs
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); // ungültig: Konstruktor von verschiedenen B-Basis-Subobjekten geerbt 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: Es gibt nur ein B-Subobjekt. // Dies initialisiert die virtuelle B-Basisklasse, // welche die A-Basisklasse initialisiert // und initialisiert dann die V1- und V2-Basisklassen // wie durch einen standardmäßigen Standardkonstruktor
Wie bei using-Deklarationen für andere nicht-statische Memberfunktionen, wenn ein geerbter Konstruktor mit der Signatur eines der Konstruktoren von
struct B1 { B1(int); }; struct B2 { B2(int); }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK: D2::D2(int) verdeckt sowohl B1::B1(int) als auch B2::B2(int) }; D2 d2(0); // ruft D2::D2(int) auf Innerhalb einer templated class , wenn eine using-Deklaration auf einen dependent name verweist, wird dies als Benennung eines Konstruktors betrachtet, falls der nested-name-specifier einen terminalen Namen hat, der mit dem unqualified-id übereinstimmt. template<class T> struct A : T { using T::T; // OK, erbt Konstruktoren von T }; template<class T, class U> struct B : T, A<U> { using A<U>::A; // OK, erbt Konstruktoren von A<U> using T::A; // erbt keine Konstruktoren von T // selbst wenn T eine Spezialisierung von A<> sein könnte }; |
(seit C++11) |
Einführung von Scoped-EnumeratorenZusätzlich zu Membern eines anderen Namespace und Membern von Basisklassen kann eine using-Deklaration auch Enumeratoren von Enumerationen in Namespace-, Block- und Klassenbereiche einführen. Eine using-Deklaration kann auch mit unscoped-Enumeratoren 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) |
Hinweise
Nur der ausdrücklich in der using-Deklaration genannte Name wird in den deklarativen Bereich übertragen: Insbesondere werden Enumeratoren nicht übertragen, wenn der Enumerationstypname mittels using-Deklaration eingeführt wird.
Eine using-Deklaration kann nicht auf einen Namespace verweisen , auf einen scoped enumerator (bis C++20) , auf einen Destruktor einer Basisklasse oder auf eine Spezialisierung eines Member-Templates für eine benutzerdefinierte Konvertierungsfunktion.
Eine using-Deklaration kann keine Spezialisierung einer Member-Template benennen ( template-id ist durch die Grammatik nicht erlaubt):
struct B { template<class T> void f(); }; struct D : B { using B::f; // OK: benennt ein Template // using B::f<int>; // Fehler: benennt eine Template-Spezialisierung void g() { f<int>(); } };
Eine using-Deklaration kann ebenfalls nicht verwendet werden, um den Namen eines abhängigen Member-Templates als einen
Template-Namen
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; // Fehler: Disambiguator nicht erlaubt using B<Y>::f; // kompiliert, aber f ist kein Template-Name void g() { // f<int>(0); // Fehler: f ist nicht als Template-Name bekannt, // daher beginnt < keine Template-Argumentliste f(0); // OK } };
Wenn eine using-Deklaration den Zuweisungsoperator der Basisklasse in die abgeleitete Klasse bringt, dessen Signatur zufällig mit dem Kopierzuweisungs- oder Verschiebezuweisungsoperator der abgeleiteten Klasse übereinstimmt, wird dieser Operator durch den implizit deklarierten Kopier-/Verschiebezuweisungsoperator der abgeleiteten Klasse verdeckt. Gleiches gilt für eine using-Deklaration, die einen Basisklassenkonstruktor erbt, der zufällig mit dem Kopier-/Verschiebekonstruktor der abgeleiteten Klasse übereinstimmt (seit C++11) .
|
Die Semantik von geerbten Konstruktoren wurde nachträglich durch einen Defect Report gegen C++11 geändert. Zuvor verursachte eine deklarierende Konstruktorvererbung eine Reihe von synthetisierten Konstruktordeklarationen, die in die abgeleitete Klasse injiziert wurden, was redundante Argumentkopien/-verschiebungen verursachte, problematische Interaktionen mit einigen Formen von SFINAE hatte und in einigen Fällen auf wichtigen ABIs nicht implementierbar sein konnte. Ältere Compiler können weiterhin die vorherige Semantik implementieren.
|
(seit C++11) |
|
Pack expansions in using-declarations ermöglichen es, eine Klasse zu bilden, die überladene Member variadischer Basen ohne Rekursion verfügbar macht: 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-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_inheriting_constructors
|
200802L
|
(C++11) | Vererbende Konstruktoren |
201511L
|
(C++17)
(DR11) |
Neuformulierung vererbender Konstruktoren | |
__cpp_variadic_using
|
201611L
|
(C++17) |
Pack-Erweiterungen
in
using
-Deklarationen
|
Schlüsselwörter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 258 | C++98 |
eine nicht-konstante Memberfunktion einer abgeleiteten Klasse kann
eine konstante Memberfunktion ihrer Basis überschreiben und/oder verbergen |
Überschreiben und Verbergen erfordern ebenfalls
gleiche CV-Qualifizierungen |
| CWG 1738 | C++11 |
es war unklar, ob es erlaubt ist,
Spezialisierungen von vererbten Konstruktor-Templates explizit zu instanziieren oder explizit zu spezialisieren |
verboten |
| CWG 2504 | C++11 |
das Verhalten von vererbten Konstruktoren
aus virtuellen Basisklassen war unklar |
klargestellt |
| P0136R1 | C++11 |
Deklaration vererbter Konstruktoren injiziert
zusätzliche Konstruktoren in die abgeleitete Klasse |
bewirkt, dass Basisklassenkonstruktoren
durch Namenssuche gefunden werden |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
-
9.9 Die
using-Deklaration [namespace.udecl]
-
9.9 Die
- C++20-Standard (ISO/IEC 14882:2020):
-
-
9.9 Die
using-Deklaration [namespace.udecl]
-
9.9 Die
- C++17-Standard (ISO/IEC 14882:2017):
-
-
10.3.3 Die
using-Deklaration [namespace.udecl]
-
10.3.3 Die
- C++14-Standard (ISO/IEC 14882:2014):
-
-
7.3.3 Die
using-Deklaration [namespace.udecl]
-
7.3.3 Die
- C++11-Standard (ISO/IEC 14882:2011):
-
-
7.3.3 Die
using-Deklaration [namespace.udecl]
-
7.3.3 Die
- C++03 Standard (ISO/IEC 14882:2003):
-
-
7.3.3 Die
usingDeklaration [namespace.udecl]
-
7.3.3 Die
- C++98 Standard (ISO/IEC 14882:1998):
-
-
7.3.3 Die
usingDeklaration [namespace.udecl]
-
7.3.3 Die