Namensräume
Varianten
Aktionen

Strukturierte Bindungsdeklaration (seit C++17)

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
 
 

Bindet die angegebenen Namen an Unterobjekte oder Elemente des Initialisierers.

Ähnlich wie eine Referenz ist eine strukturierte Bindung ein Alias für ein vorhandenes Objekt. Im Gegensatz zu einer Referenz muss eine strukturierte Bindung keinen Referenztyp haben.

attr (optional) decl-specifier-seq ref-qualifier (optional) [ sb-identifier-list ] initializer ;
attr - Sequenz einer beliebigen Anzahl von Attributen
decl-specifier-seq - Sequenz der folgenden Spezifizierer (gemäß den Regeln der einfachen Deklaration)
(seit C++26)
ref-qualifier - entweder & oder &&
sb-identifier-list - Liste von durch Kommas getrennten Bezeichnern, die durch diese Deklaration eingeführt werden, jedem Bezeichner kann eine Attribut-Spezifizierer-Sequenz folgen(seit C++26)
initializer - ein Initialisierer (siehe unten)


initializer kann einer der folgenden sein

= Ausdruck (1)
{ expression } (2)
( expression ) (3)
expression - beliebiger Ausdruck (außer nicht geklammerte Komma-Ausdrücke)


Eine strukturierte Bindungsdeklaration führt alle Bezeichner in der sb-identifier-list als Namen im umgebenden Geltungsbereich ein und bindet sie an Unterobjekte oder Elemente des durch expression bezeichneten Objekts. Die so eingeführten Bindungen werden strukturierte Bindungen genannt.

Einer der Bezeichner in der sb-identifier-list kann ein Ellipsensymbol vorangestellt sein. Ein solcher Bezeichner führt ein strukturierte Bindungspack ein.

Der Bezeichner muss eine vorlagenbezogene Entität deklarieren.

(seit C++26)

Eine strukturierte Bindung ist ein Bezeichner in der sb-identifier-list  der kein Ellipsensymbol vorangestellt ist, oder ein Element eines strukturierte Bindungspacks, das in derselben Bezeichnerliste eingeführt wurde(seit C++26).

Inhalt

[bearbeiten] Bindeprozess

Eine strukturierte Bindungsdeklaration führt zunächst eine Variable mit eindeutigem Namen (hier bezeichnet als e) ein, um den Wert des Initialisierers zu speichern, wie folgt:

  • Wenn expression einen Array-Typ cv1 A hat und keine ref-qualifier vorhanden ist, definieren Sie e als attr (optional) specifiers A e;, wobei specifiers eine Sequenz der Spezifizierer in decl-specifier-seq ist, mit Ausnahme von auto.
Dann wird jedes Element von e aus dem entsprechenden Element von expression gemäß der Form von initializer  initialisiert.
  • Andernfalls definieren Sie e als attr (optional) decl-specifier-seq ref-qualifier (optional) e initializer ;.

Wir verwenden E, um den Typ des Bezeichnerausdrucks e zu bezeichnen (d. h. E ist äquivalent zu std::remove_reference_t<decltype((e))>).

Eine strukturierte Bindungsgröße von E ist die Anzahl der strukturierten Bindungen, die durch die strukturierte Bindungsdeklaration eingeführt werden müssen.

Die Anzahl der Bezeichner in sb-identifier-list muss gleich der strukturierten Bindungsgröße von E sein.

(bis C++26)

Gegeben die Anzahl der Bezeichner in sb-identifier-list als N und die strukturierte Bindungsgröße von E als S

  • Wenn kein strukturierte Bindungspack 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 strukturierte Bindungspacks ist 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 refer to x, y, z, respectively
    auto [d, ...e] = C(); // OK: d refers to x; ...e refers to y and z
    auto [...f, g] = C(); // OK: ...f refers x and y; g refers to z
    auto [h, i, j, ...k] = C();    // OK: the pack k is empty
    auto [l, m, n, o, ...p] = C(); // error: structured binding size is too small
}

Eine strukturierte Bindungsdeklaration führt die Bindung auf eine von drei möglichen Arten 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 Klassentyp ist, der kein Union ist, und std::tuple_size<E> ein vollständiger Typ mit einem Member namens value ist (unabhängig vom Typ oder der Zugänglichkeit eines solchen Members), dann wird das "tupelähnliche" Bindungsprotokoll verwendet.
  • Fall 3: Wenn E ein Klassentyp ist, der kein Union ist, aber std::tuple_size<E> kein vollständiger Typ ist, dann werden die Namen an die zugänglichen Datenmember von E gebunden.

Jeder der drei Fälle wird unten detaillierter beschrieben.

Jede strukturierte Bindung hat einen Referenztyp, der in der untenstehenden Beschreibung definiert ist. Dieser Typ ist der Typ, der von decltype zurückgegeben wird, wenn er auf eine unparenthetische strukturierte Bindung angewendet wird.

[bearbeiten] Fall 1: Array binden

Jede strukturierte Bindung in der sb-identifier-list wird zum Namen eines lvalue, das auf das entsprechende Element des Arrays verweist. Die strukturierte Bindungsgröße von E ist gleich der Anzahl der Array-Elemente.

Der Referenztyp 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;    // creates e[2], copies a into e,
                    // then x refers to e[0], y refers to e[1]
auto& [xr, yr] = a; // xr refers to a[0], yr refers to a[1]

[bearbeiten] Fall 2: Typ binden, der Tupeloperationen implementiert

Der Ausdruck std::tuple_size<E>::value muss ein wohlgeformter ganzzahliger konstanten Ausdruck sein, und die strukturierte Bindungsgröße von E ist gleich std::tuple_size<E>::value.

Für jede strukturierte Bindung wird eine Variable mit dem Typ "Referenz auf std::tuple_element<I, E>::type" eingeführt: lvalue-Referenz, wenn ihr entsprechender Initialisierer ein lvalue ist, andernfalls rvalue-Referenz. Der Initialisierer für die I-te Variable ist

  • e.get<I>(), wenn die Suche nach dem Bezeichner get im Geltungsbereich von E mittels Klassenmember-Zugriffssuche mindestens eine Deklaration findet, die eine Funktion-Template ist, deren erster Template-Parameter ein Nicht-Typ-Parameter ist
  • Andernfalls get<I>(e), wobei get nur mittels argumentabhängiger Suche (ADL) gesucht wird, wobei Nicht-ADL-Suchen ignoriert werden.

In diesen Initialisiererausdrücken ist e ein lvalue, wenn der Typ der Entität e eine lvalue-Referenz ist (dies geschieht nur, wenn der ref-qualifier & ist oder wenn er && ist und der Initialisiererausdruck ein lvalue ist) und andernfalls ein xvalue (dies führt effektiv eine Art Perfect Forwarding durch). I ist ein std::size_t PRvalue und <I> wird immer als Template-Parameterliste interpretiert.

Die Variable hat die gleiche Speicherdauer wie e.

Die strukturierte Bindung wird dann zum Namen eines lvalue, das auf das mit besagter Variable gebundene Objekt verweist.

Der Referenztyp 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 names a structured binding that refers to x (initialized from get<0>(tpl))
// decltype(a) is std::tuple_element<0, Tpl>::type, i.e. float&
// b names a structured binding that refers to y (initialized from get<1>(tpl))
// decltype(b) is std::tuple_element<1, Tpl>::type, i.e. char&&
// c names a structured binding that refers to the third component of tpl, get<2>(tpl)
// decltype(c) is std::tuple_element<2, Tpl>::type, i.e. const int

[bearbeiten] Fall 3: An Datenmember binden

Jeder nicht-statische Datenmember von E muss ein direkter Member von E oder eine Basisklasse von E sein und muss im Kontext der strukturierten Bindung wohlgeformt sein, wenn er als e.name benannt wird. E darf keinen anonymen Union-Member haben. Die strukturierte Bindungsgröße von E ist gleich der Anzahl der nicht-statischen Datenmember.

Jede strukturierte Bindung in sb-identifier-list wird zum Namen eines lvalue, das auf den nächsten Member von e in Deklarationsreihenfolge verweist (Bitfelder werden unterstützt); der Typ des lvalue ist der von e.mI, wobei mI sich auf den I-ten Member bezieht.

Der Referenztyp der I-ten strukturierten Bindung ist der Typ von e.mI, wenn dieser kein Referenztyp ist, oder der deklarierte Typ von mI andernfalls.

#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 is an int lvalue identifying the 2-bit bit-field
                             // y is a const volatile double lvalue
    std::cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // Error: y is const-qualified
    std::cout << x << ' ' << y << '\n';  // -2 2.3
}

[bearbeiten] Initialisierungsreihenfolge

Sei valI das Objekt oder die Referenz, die durch die I-te strukturierte Bindung 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.

[bearbeiten] 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 wie üblich die Zugänglichkeit und auch den exakten Typ des Nicht-Typ-Template-Parameters. Ein privater template<char*> void get(); Member führt zur Verwendung der Member-Interpretation, auch wenn diese fehlerhaft ist.

Der Teil der Deklaration vor [ gilt für die versteckte Variable e, nicht für die eingeführten strukturierten Bindungen.

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x and y are of type int&
auto [z, w] = std::tie(a, b);        // z and w are still of type int&
assert(&z == &a);                    // passes

Die Tupel-ähnliche Interpretation wird immer verwendet, wenn std::tuple_size<E> ein vollständiger Typ mit einem Member namens value ist, auch wenn dies dazu führen würde, dass das Programm fehlerhaft ist.

struct A { int x; };
 
namespace std
{
    template<>
    struct tuple_size<::A> { void value(); };
}
 
auto [x] = A{}; // error; the "data member" interpretation is not considered.

Die üblichen Regeln für die Referenzbindung an temporäre Objekte (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 aus dem PRvalue-Ausdruck materialisiert wurde, und ihre Lebensdauer verlängert. Wie üblich schlägt die Bindung fehl, wenn e eine nicht-const lvalue-Referenz ist.

int a = 1;
 
const auto& [x] = std::make_tuple(a); // OK, not dangling
auto&       [y] = std::make_tuple(a); // error, cannot bind auto& to rvalue std::tuple
auto&&      [z] = std::make_tuple(a); // also OK

decltype(x), wobei x eine strukturierte Bindung bezeichnet, nennt den Referenztyp dieser strukturierten Bindung. Im Tupel-ähnlichen Fall ist dies der von std::tuple_element zurückgegebene Typ, der möglicherweise keine Referenz ist, obwohl in diesem Fall immer eine versteckte Referenz eingeführt wird. Dies emuliert effektiv das Verhalten der Bindung an eine Struktur, deren nicht-statische Datenmember die von std::tuple_element zurückgegebenen Typen haben, wobei die Referenzhaftigkeit der Bindung selbst nur ein Implementierungsdetail ist.

std::tuple<int, int&> f();
 
auto [x, y] = f();       // decltype(x) is int
                         // decltype(y) is int&
 
const auto [z, w] = f(); // decltype(z) is const int
                         // decltype(w) is int&

Strukturierte Bindungen 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 betragen, solange die sb-identifier-list genau einen Bezeichner enthält, der nur ein leeres strukturierte Bindungspack 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-Testmakro 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

[bearbeiten] Schlüsselwörter

auto

[bearbeiten] 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

[bearbeiten] 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 könnte sich auf die Namen aus identifier-list beziehen die Deklaration ist
ill-formed in diesem Fall
CWG 2312 C++17 die Bedeutung von mutable ging in Fall 3 verloren seine Bedeutung ist immer noch erhalten
CWG 2313 C++17 in Fall 2 könnten die strukturierte Bindungsvariablen neu deklariert werden können nicht neu deklariert werden
CWG 2339 C++17 in Fall 2 fehlte die Definition von I die Definition wurde hinzugefügt
CWG 2341
(P1091R3)
C++17 strukturierte Bindungen konnten nicht
mit statischer Speicherdauer deklariert werden
erlaubt
CWG 2386 C++17 das "tupelähnliche" Bindungsprotokoll wurde verwendet
jedes Mal, wenn std::tuple_size<E> ein vollständiger Typ ist
nur verwendet, wenn std::tuple_size<E>
einen Member value hat
CWG 2506 C++17 wenn expression vom Typ eines cv-qualifizierten Arrays ist,
wurde die cv-Qualifizierung auf E übertragen
verwirft diese cv-Qualifizierung
CWG 2635 C++20 strukturierte Bindungen könnten eingeschränkt werden verboten
CWG 2867 C++17 die Initialisierungsreihenfolge war unklar wurde klargestellt
P0961R1 C++17 in Fall 2 wurde der Member get verwendet
wenn die Suche eine get jeglicher Art findet
nur wenn die Suche eine Funktion findet
Template mit einem Nicht-Typ-Parameter
P0969R0 C++17 in Fall 3 mussten die Member öffentlich sein nur zugänglich sein müssen
im Kontext der Deklaration

[bearbeiten] 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 Strukturierte Bindungsdeklarationen [dcl.struct.bind] (S. 219-220)
  • C++17 Standard (ISO/IEC 14882:2017)
  • 11.5 Strukturierte Bindungsdeklarationen [dcl.struct.bind] (S. 219-220)

[bearbeiten] Siehe auch

(C++11)
erstellt ein tuple von Lvalue-Referenzen oder entpackt ein Tupel in einzelne Objekte
(Funktionsvorlage) [edit]