Namespaces
Variants

Structured binding declaration (since C++17)

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

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 ):
(seit C++26)
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 A hat und kein ref-qualifier vorhanden ist, definiere e als attr  (optional) specifiers A 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:
  • Andernfalls definiere e als attr  (optional) decl-specifier-seq ref-qualifier  (optional) e initializer  ; .

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 E sein.

(bis C++26)

Gegeben sei die Anzahl der Bezeichner in sb-identifier-list als N und die Structured Binding-Größe von E als S :

  • Wenn kein Structured Binding-Pack vorhanden ist, muss N gleich S sein.
  • Andernfalls muss die Anzahl der Nicht-Pack-Elemente (d.h. N - 1 ) kleiner oder gleich S sein, und die Anzahl der Elemente des Structured Binding-Packs beträgt S - N + 1 (was null sein kann).
(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 E ein Array-Typ ist, dann werden die Namen an die Array-Elemente gebunden.
  • Fall 2: Wenn E ein Nicht-Union-Klassentyp ist und std:: tuple_size < E > ein vollständiger Typ mit einem Mitglied namens value ist (unabhängig vom Typ oder der Zugänglichkeit dieses Members), dann wird das "tupelartige" Bindungsprotokoll verwendet.
  • Fall 3: Wenn E ein Nicht-Union-Klassentyp ist, aber std:: tuple_size < E > kein vollständiger Typ ist, dann werden die Namen an die zugänglichen Datenmitglieder von E gebunden.

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 get im Gültigkeitsbereich von E durch 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:

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x und y sind vom Typ int&
auto [z, w] = std::tie(a, b);        // z und w sind immer noch vom Typ int&
assert(&z == &a);                    // erfolgreich

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

auto

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)