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

Связывает указанные имена с подобъектами или элементами инициализатора.

Как и ссылка, структурная привязка является псевдонимом существующего объекта. В отличие от ссылки, структурная привязка не обязательно должна быть ссылочного типа.

attr  (необязательно) decl-specifier-seq ref-qualifier  (необязательно) [ sb-identifier-list ] initializer  ;
attr - последовательность любого количества атрибутов
decl-specifier-seq - последовательность следующих спецификаторов (согласно правилам простого объявления ):
(начиная с C++26)
ref-qualifier - либо & , либо &&
sb-identifier-list - список идентификаторов, разделённых запятыми, вводимых этим объявлением , каждый идентификатор может сопровождаться последовательностью спецификаторов атрибутов (начиная с C++26)
initializer - инициализатор (см. ниже)


initializer может быть одним из следующих:

= выражение (1)
{ выражение } (2)
( выражение ) (3)
expression - любое выражение (кроме неподобных comma expressions )


Декларация структурированной привязки вводит все идентификаторы в sb-identifier-list как имена в окружающей области видимости и связывает их с подобъектами или элементами объекта, обозначаемого expression . Связи, введенные таким образом, называются структурированными привязками .

Один из идентификаторов в sb-identifier-list может предваряться многоточием. Такой идентификатор вводит structured binding pack .

Идентификатор должен объявлять templated entity .

(since C++26)

Структурированная привязка - это идентификатор в sb-identifier-list  который не предваряется многоточием, или элемент пакета структурированных привязок, введённый в том же списке идентификаторов (начиная с C++26) .

Содержание

Процесс связывания

Декларация структурированной привязки сначала вводит уникально именованную переменную (здесь обозначенную как e ) для хранения значения инициализатора, следующим образом:

  • Если expression имеет тип массива cv1 A и отсутствует ref-qualifier , определите e как attr  (опционально) specifiers A e; , где specifiers является последовательностью спецификаторов из decl-specifier-seq исключая auto .
Затем каждый элемент e инициализируется из соответствующего элемента expression в соответствии с формой initializer :
  • Для синтаксиса инициализации (1) , элементы copy-initialized .
  • Для синтаксисов инициализации (2,3) , элементы direct-initialized .
  • В противном случае, определите e как attr  (optional) decl-specifier-seq ref-qualifier  (optional) e initializer  ; .

Мы используем E для обозначения типа выражения идентификатора e (т.е., E эквивалентно std:: remove_reference_t < decltype ( ( e ) ) > ).

Размер структурированной привязки для E — это количество структурированных привязок, которые необходимо ввести объявлением структурированной привязки.

Количество идентификаторов в sb-identifier-list должно быть равно размеру структурированной привязки E .

(до C++26)

Пусть количество идентификаторов в sb-identifier-list равно N , а размер структурированной привязки E равен S :

  • Если нет пакета структурированной привязки, N должно быть равно S .
  • В противном случае количество непакетных элементов (т.е. N - 1 ) должно быть меньше или равно S , а количество элементов пакета структурированной привязки равно S - N + 1 (что может быть нулевым).
(начиная с 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 ссылаются на x, y, z соответственно
    auto [d, ...e] = C(); // OK: d ссылается на x; ...e ссылается на y и z
    auto [...f, g] = C(); // OK: ...f ссылается на x и y; g ссылается на z
    auto [h, i, j, ...k] = C();    // OK: пакет k пуст
    auto [l, m, n, o, ...p] = C(); // ошибка: размер структурированной привязки слишком мал
}

Декларация структурированной привязки выполняет связывание одним из трех возможных способов, в зависимости от E :

  • Случай 1: Если E является типом массива, то имена связываются с элементами массива.
  • Случай 2: Если E является типом класса (не объединением) и std:: tuple_size < E > является полным типом с членом value (независимо от типа или доступности такого члена), то используется протокол привязки "по образцу кортежа".
  • Случай 3: Если E является типом класса (не объединением), но std:: tuple_size < E > не является полным типом, то имена связываются с доступными членами-данными E .

Каждый из трёх случаев описан более подробно ниже.

Каждое структурное связывание имеет ссылочный тип , определяемый в описании ниже. Этот тип является типом, возвращаемым decltype при применении к непомещенному в скобки структурному связыванию.

Случай 1: привязка массива

Каждая структурная привязка в sb-identifier-list становится именем lvalue, которое ссылается на соответствующий элемент массива. Размер структурной привязки E равен количеству элементов массива.

Ссылочный тип для каждого структурированного связывания является типом элемента массива. Обратите внимание, что если тип массива E является cv-квалифицированным, то таким же является и его тип элемента.

int a[2] = {1, 2};
auto [x, y] = a;    // создает e[2], копирует a в e,
                    // затем x ссылается на e[0], y ссылается на e[1]
auto& [xr, yr] = a; // xr ссылается на a[0], yr ссылается на a[1]

Случай 2: привязка типа, реализующего операции с кортежами

Выражение std:: tuple_size < E > :: value должно быть корректным целочисленным константным выражением , а размер структурированной привязки E равен std:: tuple_size < E > :: value .

Для каждого структурированного связывания вводится переменная, тип которой — «ссылка на std:: tuple_element < I, E > :: type »: lvalue-ссылка, если соответствующий инициализатор является lvalue, иначе rvalue-ссылка. Инициализатор для I -й переменной равен

  • e. get < I > ( ) , если поиск идентификатора get в области видимости E через поиск доступа к членам класса находит хотя бы одно объявление, которое является шаблоном функции, чей первый параметр шаблона является константным параметром
  • В противном случае, get < I > ( e ) , где get ищется только через поиск, зависимый от аргументов , игнорируя поиск без ADL.

В этих инициализирующих выражениях, e является lvalue, если тип сущности e является ссылкой на lvalue (это происходит только если ref-qualifier имеет значение & или если это && и инициализирующее выражение является lvalue), и xvalue в противном случае (это фактически выполняет своего рода идеальную пересылку), I является prvalue типа std::size_t , а < I > всегда интерпретируется как список параметров шаблона.

Переменная имеет ту же storage duration , что и e .

Структурированная привязка затем становится именем lvalue, которое ссылается на объект, привязанный к указанной переменной.

Ссылочный тип для I -го структурированного связывания это 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 - это структурированная привязка, ссылающаяся на x (инициализирована из get<0>(tpl))
// decltype(a) - это std::tuple_element<0, Tpl>::type, т.е. float&
// b - это структурированная привязка, ссылающаяся на y (инициализирована из get<1>(tpl))
// decltype(b) - это std::tuple_element<1, Tpl>::type, т.е. char&&
// c - это структурированная привязка, ссылающаяся на третий компонент tpl, get<2>(tpl)
// decltype(c) - это std::tuple_element<2, Tpl>::type, т.е. const int

Случай 3: привязка к членам данных

Каждый нестатический член данных E должен быть прямым членом E или одного базового класса E и должен быть корректным в контексте структурированной привязки при обращении как e. name . E не может содержать анонимный объединение. Размер структурированной привязки E равен количеству нестатических членов данных.

Каждое структурное связывание в sb-identifier-list становится именем lvalue, которое ссылается на следующий член e в порядке объявления (битовые поля поддерживаются); тип lvalue соответствует типу e. mI , где mI ссылается на I -й член.

Ссылочный тип I-го структурированного связывания является типом e. mI если он не является ссылочным типом, или объявленным типом 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 - это lvalue типа int, ссылающееся на 2-битовое битовое поле
                             // y - это const volatile lvalue типа double
    std::cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // Ошибка: y имеет квалификатор const
    std::cout << x << ' ' << y << '\n';  // -2 2.3
}

Порядок инициализации

Пусть valI будет объектом или ссылкой, обозначенной I -м структурированным связыванием в sb-identifier-list :

  • Инициализация e выполняется до упорядочения инициализации любого valI .
  • Инициализация каждого valI выполняется до упорядочения инициализации любого valJ , где I меньше J .

Примечания

Структурированные привязки не могут быть ограничены :

template<class T>
concept C = true;
C auto [x, y] = std::pair{1, 2}; // error: constrained
(начиная с C++20)

Поиск члена get игнорирует доступность, как обычно, а также игнорирует точный тип параметра шаблона-константы. Приватный template < char * > void get ( ) ; член приведет к использованию интерпретации члена, даже если это некорректно сформировано.

Часть объявления, предшествующая [ относится к скрытой переменной e , а не к введенным структурированным привязкам:

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x и y имеют тип int&
auto [z, w] = std::tie(a, b);        // z и w всё ещё имеют тип int&
assert(&z == &a);                    // проходит

Кортежеподобная интерпретация всегда используется, если std:: tuple_size < E > является полным типом с членом value , даже если это приведёт к некорректности программы:

struct A { int x; };
namespace std
{
    template<>
    struct tuple_size<::A> { void value(); };
}
auto [x] = A{}; // ошибка; интерпретация "член данных" не рассматривается.

Обычные правила привязки ссылок к временным объектам (включая продление времени жизни) применяются, если присутствует ref-qualifier и expression является prvalue. В этих случаях скрытая переменная e является ссылкой, которая привязывается к временной переменной, материализованной из prvalue-выражения, продлевая её время жизни. Как обычно, привязка не удастся, если e является неконстантной lvalue-ссылкой:

int a = 1;
const auto& [x] = std::make_tuple(a); // OK, не висячая ссылка
auto&       [y] = std::make_tuple(a); // ошибка, нельзя привязать auto& к rvalue std::tuple
auto&&      [z] = std::make_tuple(a); // также OK

decltype ( x ) , где x обозначает структурную привязку, называет ссылочный тип этой структурной привязки. В случае tuple-подобного типа это тип, возвращаемый std::tuple_element , который может не быть ссылкой, даже если в этом случае всегда вводится скрытая ссылка. Это эффективно эмулирует поведение привязки к структуре, нестатические члены данных которой имеют типы, возвращаемые std::tuple_element , при этом ссылочность самой привязки является лишь деталью реализации.

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

Структурированные привязки не могут быть захвачены лямбда-выражениями :

#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);
}
(до C++20)


Размер структурированной привязки может быть равным 0 при условии, что sb-identifier-list содержит ровно один идентификатор, который может вводить только пустой пакет структурированной привязки.

auto return_empty() -> std::tuple<>;
template <class>
void test_empty()
{
    auto [] = return_empty(); // ошибка
    auto [...args] = return_empty(); // OK, args - пустой пакет
    auto [one, ...rest] = return_empty(); // ошибка, размер структурированной привязки слишком мал
}
(начиная с C++26)
Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_structured_bindings 201606L (C++17) Структурированные привязки
202403L (C++26) Структурированные привязки с атрибутами
202406L (C++26) Объявление структурированной привязки как условие
202411L (C++26) Структурированные привязки могут вводить пакет

Ключевые слова

auto

Пример

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

Вывод:

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

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение как опубликовано Корректное поведение
CWG 2285 C++17 expression мог ссылаться на имена из identifier-list объявление является
некорректным в этом случае
CWG 2312 C++17 значение mutable было потеряно в случае 3 его значение сохраняется
CWG 2313 C++17 в случае 2, переменные структурной привязки могли быть переобъявлены не могут быть переобъявлены
CWG 2339 C++17 в случае 2, определение I отсутствовало добавлено определение
CWG 2341
( P1091R3 )
C++17 структурные привязки не могли быть
объявлены со статической продолжительностью хранения
разрешено
CWG 2386 C++17 протокол "tuple-like" привязки использовался
всякий раз, когда std:: tuple_size < E > является полным типом
используется только когда std:: tuple_size < E >
имеет член value
CWG 2506 C++17 если expression имеет тип cv-квалифицированного массива,
cv-квалификация переносилась на E
отбрасывает эту cv-квалификацию
CWG 2635 C++20 структурные привязки могли быть ограниченными запрещено
CWG 2867 C++17 порядок инициализации был неясен прояснён
P0961R1 C++17 в случае 2, член get использовался
если поиск находит get любого вида
только если поиск находит шаблон функции
с постоянным параметром
P0969R0 C++17 в случае 3, члены требовались быть публичными требуется только доступность
в контексте объявления

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 9.6 Декларации структурированных привязок [dcl.struct.bind] (стр: 228-229)
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 9.6 Декларации структурированных привязок [dcl.struct.bind] (стр: 219-220)
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 11.5 Декларации структурированных привязок [dcl.struct.bind] (стр: 219-220)

Смотрите также

(C++11)
создает tuple ссылок на lvalue или распаковывает кортеж в отдельные объекты
(шаблон функции)