Structured binding declaration (since C++17)
Bindet die angegebenen Namen an Unterobjekte oder Elemente des Initialisierers.
Wie eine Referenz ist eine strukturierte Bindung ein Alias für ein bestehendes Objekt. Im Gegensatz zu einer Referenz muss eine strukturierte Bindung nicht vom Referenztyp sein.
attr
(optional)
decl-specifier-seq
ref-qualifier
(optional)
[
sb-identifier-list
]
initializer
;
|
|||||||||
| attr | - | Folge beliebig vieler Attribute | ||
| decl-specifier-seq | - |
Folge der folgenden Spezifizierer (gemäß den Regeln der
einfachen Deklaration
):
|
||
| ref-qualifier | - |
entweder
&
oder
&&
|
||
| sb-identifier-list | - | Liste durch Kommas getrennter Bezeichner, die durch diese Deklaration eingeführt werden , jeder Bezeichner kann von einer Attributspezifizierer-Sequenz gefolgt werden (seit C++26) | ||
| initializer | - | ein Initialisierer (siehe unten) |
initializer
kann einer der folgenden sein:
=
Ausdruck
|
(1) | ||||||||
{
Ausdruck
}
|
(2) | ||||||||
(
Ausdruck
)
|
(3) | ||||||||
| expression | - | beliebiger Ausdruck (außer nicht in Klammern gesetzte Komma-Ausdrücke ) |
Eine strukturierte Bindungsdeklaration führt alle Bezeichner in der
sb-identifier-list
als Namen im umgebenden Gültigkeitsbereich ein und bindet sie an Teilobjekte oder Elemente des durch
expression
bezeichneten Objekts. Die so eingeführten Bindungen werden als
strukturierte Bindungen
bezeichnet.
|
Einer der Bezeichner in der sb-identifier-list kann von einer Ellipse gefolgt werden. Ein solcher Bezeichner führt ein structured binding pack ein. Der Bezeichner muss eine templated entity deklarieren. |
(since C++26) |
Eine strukturierte Bindung ist ein Bezeichner in der sb-identifier-list der nicht von einer Ellipse eingeleitet wird, oder ein Element eines strukturierten Bindungs-Packs, das in derselben Bezeichnerliste eingeführt wird (seit C++26) .
Inhaltsverzeichnis |
Bindungsprozess
Eine strukturierte Bindungsdeklaration führt zunächst eine eindeutig benannte Variable (hier bezeichnet durch e ) ein, um den Wert des Initialisierers zu halten, wie folgt:
-
Wenn
expression
Array-Typ
cv1
Ahat und kein ref-qualifier vorhanden ist, definiere e als attr (optional) specifiersA e;, wobei specifiers eine Sequenz der Spezifizierer in decl-specifier-seq ist, ausgenommen auto .
-
Dann wird jedes Element von
e
aus dem entsprechenden Element von
expression
entsprechend der Form des
initializer
initialisiert:
- Für die Initialisierer-Syntax (1) werden die Elemente copy-initialized .
- Für die Initialisierer-Syntaxen (2,3) werden die Elemente direct-initialized .
-
Andernfalls definiere
e
als
attr
(optional)
decl-specifier-seq
ref-qualifier
(optional)
einitializer ;.
Wir verwenden
E
, um den Typ des Identifikatorausdrucks
e
zu bezeichnen (d.h.,
E
entspricht
std::
remove_reference_t
<
decltype
(
(
e
)
)
>
).
Eine
structured binding size
von
E
ist die Anzahl der structured bindings, die durch die structured binding Deklaration eingeführt werden müssen.
|
Die Anzahl der Bezeichner in
sb-identifier-list
muss gleich der Structured Binding-Größe von
|
(bis C++26) |
|
Gegeben sei die Anzahl der Bezeichner in
sb-identifier-list
als
N
und die Structured Binding-Größe von
|
(seit C++26) |
struct C { int x, y, z; }; template<class T> void now_i_know_my() { auto [a, b, c] = C(); // OK: a, b, c verweisen auf x, y, z, jeweils auto [d, ...e] = C(); // OK: d verweist auf x; ...e verweist auf y und z auto [...f, g] = C(); // OK: ...f verweist auf x und y; g verweist auf z auto [h, i, j, ...k] = C(); // OK: das Paket k ist leer auto [l, m, n, o, ...p] = C(); // Fehler: Größe der strukturierten Bindung ist zu klein }
Eine strukturierte Bindungsdeklaration führt die Bindung auf eine von drei möglichen Weisen durch, abhängig von
E
:
-
Fall 1: Wenn
Eein Array-Typ ist, dann werden die Namen an die Array-Elemente gebunden. -
Fall 2: Wenn
Eein Nicht-Union-Klassentyp ist und std:: tuple_size < E > ein vollständiger Typ mit einem Mitglied namensvalueist (unabhängig vom Typ oder der Zugänglichkeit dieses Members), dann wird das "tupelartige" Bindungsprotokoll verwendet. -
Fall 3: Wenn
Eein Nicht-Union-Klassentyp ist, aber std:: tuple_size < E > kein vollständiger Typ ist, dann werden die Namen an die zugänglichen Datenmitglieder vonEgebunden.
Jeder der drei Fälle wird nachstehend ausführlicher beschrieben.
Jede strukturierte Bindung hat einen
referenzierten Typ
, definiert in der folgenden Beschreibung. Dieser Typ ist der Typ, der von
decltype
zurückgegeben wird, wenn es auf eine nicht in Klammern gesetzte strukturierte Bindung angewendet wird.
Fall 1: Binden eines Arrays
Jede strukturierte Bindung in der
sb-identifier-list
wird zum Namen eines Lvalues, das auf das entsprechende Element des Arrays verweist. Die strukturierte Bindungsgröße von
E
entspricht der Anzahl der Array-Elemente.
Der
referenzierte Typ
für jede strukturierte Bindung ist der Array-Elementtyp. Beachten Sie, dass wenn der Array-Typ
E
cv-qualifiziert ist, dies auch für seinen Elementtyp gilt.
int a[2] = {1, 2}; auto [x, y] = a; // erstellt e[2], kopiert a nach e, // dann referenziert x auf e[0], y referenziert auf e[1] auto& [xr, yr] = a; // xr referenziert auf a[0], yr referenziert auf a[1]
Fall 2: Bindung eines Typs, der die Tupeloperationen implementiert
Der Ausdruck
std::
tuple_size
<
E
>
::
value
muss ein wohlgeformter
integraler konstanter Ausdruck
sein, und die strukturierte Bindungsgröße von
E
ist gleich
std::
tuple_size
<
E
>
::
value
.
Für jede strukturierte Bindung wird eine Variable eingeführt, deren Typ "Referenz auf std:: tuple_element < I, E > :: type " ist: Lvalue-Referenz, falls der entsprechende Initialisierer ein Lvalue ist, andernfalls Rvalue-Referenz. Der Initialisierer für die I -te Variable ist
-
e.
get
<
I
>
(
)
, falls die Suche nach dem Bezeichner
getim Gültigkeitsbereich vonEdurch Klassenmitglied-Zugriffssuche mindestens eine Deklaration findet, die eine Funktionsvorlage ist, deren erster Template-Parameter ein konstanter Parameter ist - Andernfalls, get < I > ( e ) , wobei get ausschließlich durch argumentabhängige Suche gesucht wird, wobei nicht-ADL-Suche ignoriert wird.
In diesen Initialisierungsausdrücken ist
e
ein Lvalue, wenn der Typ der Entität
e
ein Lvalue-Referenztyp ist (dies tritt nur auf, wenn der
Ref-Qualifier
entweder
&
ist oder wenn er
&&
ist und der Initialisierungsausdruck ein Lvalue ist), andernfalls ein Xvalue (dies führt effektiv eine Art perfektes Forwarding durch).
I
ist ein
std::size_t
Prvalue, und
<
I
>
wird stets als Template-Parameterliste interpretiert.
Die Variable hat dieselbe storage duration wie e .
Die strukturierte Bindung wird dann zum Namen eines Lvalues, der sich auf das an diese Variable gebundene Objekt bezieht.
Der referenzierte Typ für die I te strukturierte Bindung ist std:: tuple_element < I, E > :: type .
float x{}; char y{}; int z{}; std::tuple<float&, char&&, int> tpl(x, std::move(y), z); const auto& [a, b, c] = tpl; // using Tpl = const std::tuple<float&, char&&, int>; // a bezeichnet eine strukturierte Bindung, die sich auf x bezieht (initialisiert von get<0>(tpl)) // decltype(a) ist std::tuple_element<0, Tpl>::type, d.h. float& // b bezeichnet eine strukturierte Bindung, die sich auf y bezieht (initialisiert von get<1>(tpl)) // decltype(b) ist std::tuple_element<1, Tpl>::type, d.h. char&& // c bezeichnet eine strukturierte Bindung, die sich auf die dritte Komponente von tpl bezieht, get<2>(tpl) // decltype(c) ist std::tuple_element<2, Tpl>::type, d.h. const int
Fall 3: Bindung an Datenmitglieder
Jeder nicht-statische Datenelement von
E
muss ein direktes Element von
E
oder dieselbe Basisklasse von
E
sein, und muss wohlgeformt im Kontext der strukturierten Bindung sein, wenn benannt als
e.
name
.
E
darf kein anonymes Union-Mitglied haben. Die strukturierte Bindungsgröße von
E
ist gleich der Anzahl der nicht-statischen Datenelemente.
Jede strukturierte Bindung in
sb-identifier-list
wird zum Namen eines L-Werts, der auf das nächste Mitglied von
e
in Deklarationsreihenfolge verweist (Bitfelder werden unterstützt); der Typ des L-Werts ist der von
e.
mI
, wobei
mI
auf das
I
-te Mitglied verweist.
Der
referenzierte Typ
des
I
-ten structured binding ist der Typ von
e.
mI
, falls es kein Referenztyp ist, oder andernfalls der deklarierte Typ von
mI
.
#include <iostream> struct S { mutable int x1 : 2; volatile double y1; }; S f() { return S{1, 2.3}; } int main() { const auto [x, y] = f(); // x ist ein int-Lvalue, der das 2-Bit-Bitfeld identifiziert // y ist ein const volatile double-Lvalue std::cout << x << ' ' << y << '\n'; // 1 2.3 x = -2; // OK // y = -2.; // Fehler: y ist const-qualifiziert std::cout << x << ' ' << y << '\n'; // -2 2.3 }
Initialisierungsreihenfolge
Sei valI das Objekt oder der Referenz, der durch das I -te strukturierte Binding in sb-identifier-list benannt wird:
- Die Initialisierung von e ist sequenziert vor der Initialisierung jedes valI .
- Die Initialisierung jedes valI ist sequenziert vor der Initialisierung jedes valJ , wobei I kleiner als J ist.
Hinweise
|
Strukturierte Bindungen können nicht eingeschränkt werden : template<class T> concept C = true; C auto [x, y] = std::pair{1, 2}; // error: constrained |
(seit C++20) |
Die Suche nach dem Member
get
ignoriert Zugriffsmöglichkeiten wie üblich und ignoriert auch den exakten Typ des konstanten Template-Parameters. Ein privater
template
<
char
*
>
void
get
(
)
;
Member führt dazu, dass die Member-Interpretation verwendet wird, obwohl sie fehlerhaft ist.
Der Teil der Deklaration vor
[
bezieht sich auf die versteckte Variable
e
, nicht auf die eingeführten strukturierten Bindungen:
Die tupelartige Interpretation wird immer verwendet, falls
std::
tuple_size
<
E
>
ein vollständiger Typ mit einem Mitglied namens
value
ist, selbst wenn dies dazu führen würde, dass das Programm fehlerhaft wäre:
struct A { int x; }; namespace std { template<> struct tuple_size<::A> { void value(); }; } auto [x] = A{}; // Fehler; die "Datenmitglied"-Interpretation wird nicht berücksichtigt.
Die üblichen Regeln für die Referenzbindung an Temporaries (einschließlich Lebensdauerverlängerung) gelten, wenn ein ref-qualifier vorhanden ist und der expression ein prvalue ist. In diesen Fällen ist die versteckte Variable e eine Referenz, die an die temporäre Variable bindet, die materialisiert wurde aus dem prvalue-Ausdruck, und verlängert deren Lebensdauer. Wie üblich schlägt die Bindung fehl, wenn e eine non-const lvalue reference ist:
int a = 1; const auto& [x] = std::make_tuple(a); // OK, kein Dangling-Referenz auto& [y] = std::make_tuple(a); // Fehler, auto& kann nicht an Rvalue std::tuple gebunden werden auto&& [z] = std::make_tuple(a); // ebenfalls OK
decltype ( x ) , wobei x eine strukturierte Bindung bezeichnet, benennt den referenzierten Typ dieser strukturierten Bindung. Im tuple-ähnlichen Fall ist dies der von std::tuple_element zurückgegebene Typ, der selbst dann keine Referenz sein muss, wenn in diesem Fall stets eine versteckte Referenz eingeführt wird. Dies emuliert effektiv das Verhalten der Bindung an eine Struktur, deren nicht-statische Datenelemente die von std::tuple_element zurückgegebenen Typen haben, wobei die Referenzqualität der Bindung selbst lediglich ein Implementierungsdetail darstellt.
std::tuple<int, int&> f(); auto [x, y] = f(); // decltype(x) ist int // decltype(y) ist int& const auto [z, w] = f(); // decltype(z) ist const int // decltype(w) ist int&
|
Strukturierte Bindings können nicht von Lambda-Ausdrücken erfasst werden: #include <cassert> int main() { struct S { int p{6}, q{7}; }; const auto& [b, d] = S{}; auto l = [b, d] { return b * d; }; // valid since C++20 assert(l() == 42); } |
(bis C++20) |
|
Eine strukturierte Bindungsgröße darf 0 sein, solange die sb-identifier-list genau einen Bezeichner enthält, der nur ein leeres strukturiertes Bindungs-Pack einführen kann. auto return_empty() -> std::tuple<>; template <class> void test_empty() { auto [] = return_empty(); // error auto [...args] = return_empty(); // OK, args is an empty pack auto [one, ...rest] = return_empty(); // error, structured binding size is too small } |
(seit C++26) |
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_structured_bindings
|
201606L
|
(C++17) | Strukturierte Bindungen |
202403L
|
(C++26) | Strukturierte Bindungen mit Attributen | |
202406L
|
(C++26) | Strukturierte Bindungsdeklaration als Bedingung | |
202411L
|
(C++26) | Strukturierte Bindungen können ein Pack einführen |
Schlüsselwörter
Beispiel
#include <iomanip> #include <iostream> #include <set> #include <string> int main() { std::set<std::string> myset{"hello"}; for (int i{2}; i; --i) { if (auto [iter, success] = myset.insert("Hello"); success) std::cout << "Insert is successful. The value is " << std::quoted(*iter) << ".\n"; else std::cout << "The value " << std::quoted(*iter) << " already exists in the set.\n"; } struct BitFields { // C++20: default member initializer for bit-fields int b : 4 {1}, d : 4 {2}, p : 4 {3}, q : 4 {4}; }; { const auto [b, d, p, q] = BitFields{}; std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n'; } { const auto [b, d, p, q] = []{ return BitFields{4, 3, 2, 1}; }(); std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n'; } { BitFields s; auto& [b, d, p, q] = s; std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n'; b = 4, d = 3, p = 2, q = 1; std::cout << s.b << ' ' << s.d << ' ' << s.p << ' ' << s.q << '\n'; } }
Ausgabe:
Insert is successful. The value is "Hello". The value "Hello" already exists in the set. 1 2 3 4 4 3 2 1 1 2 3 4 4 3 2 1
Fehlerberichte
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 2285 | C++17 | expression konnte auf Namen aus identifier-list verweisen |
die Deklaration ist
in diesem Fall fehlerhaft |
| CWG 2312 | C++17 | die Bedeutung von mutable ging in Fall 3 verloren | ihre Bedeutung bleibt erhalten |
| CWG 2313 | C++17 | in Fall 2 konnten die Strukturbindungsvariablen erneut deklariert werden | können nicht erneut deklariert werden |
| CWG 2339 | C++17 | in Fall 2 fehlte die Definition von I | Definition hinzugefügt |
|
CWG 2341
( P1091R3 ) |
C++17 |
Strukturbindungen konnten nicht
mit statischer Speicherdauer deklariert werden |
erlaubt |
| CWG 2386 | C++17 |
das "Tupel-ähnliche" Bindungsprotokoll wurde verwendet
wann immer std:: tuple_size < E > ein vollständiger Typ ist |
nur verwendet wenn
std::
tuple_size
<
E
>
ein Mitglied
value
hat
|
| CWG 2506 | C++17 |
wenn
expression
von einem cv-qualifizierten Array-Typ ist,
wurde die CV-Qualifizierung auf
E
übertragen
|
verwirft diese CV-Qualifizierung |
| CWG 2635 | C++20 | Strukturbindungen konnten eingeschränkt werden | verboten |
| CWG 2867 | C++17 | die Initialisierungsreihenfolge war unklar | klargestellt |
| P0961R1 | C++17 |
in Fall 2 wurde Mitglied
get
verwendet
wenn die Suche ein
get
jeglicher Art findet
|
nur wenn die Suche eine Funktions-
vorlage mit einem konstanten Parameter findet |
| P0969R0 | C++17 | in Fall 3 mussten die Mitglieder öffentlich sein |
nur im Kontext der Deklaration
zugänglich sein müssen |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 9.6 Strukturierte Bindungsdeklarationen [dcl.struct.bind] (S: 228-229)
- C++20-Standard (ISO/IEC 14882:2020):
-
- 9.6 Strukturbindungsdeklarationen [dcl.struct.bind] (S: 219-220)
- C++17-Standard (ISO/IEC 14882:2017):
-
- 11.5 Strukturierte Bindungsdeklarationen [dcl.struct.bind] (S: 219-220)
Siehe auch
|
(C++11)
|
erstellt ein
tuple
aus Lvalue-Referenzen oder entpackt ein Tupel in einzelne Objekte
(Funktions-Template) |