Structured binding declaration (since C++17)
Связывает указанные имена с подобъектами или элементами инициализатора.
Как и ссылка, структурная привязка является псевдонимом существующего объекта. В отличие от ссылки, структурная привязка не обязательно должна быть ссылочного типа.
attr
(необязательно)
decl-specifier-seq
ref-qualifier
(необязательно)
[
sb-identifier-list
]
initializer
;
|
|||||||||
| attr | - | последовательность любого количества атрибутов | ||
| decl-specifier-seq | - |
последовательность следующих спецификаторов (согласно правилам
простого объявления
):
|
||
| 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 (опционально) specifiersA 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)
einitializer ;.
Мы используем
E
для обозначения типа выражения идентификатора
e
(т.е.,
E
эквивалентно
std::
remove_reference_t
<
decltype
(
(
e
)
)
>
).
Размер структурированной привязки
для
E
— это количество структурированных привязок, которые необходимо ввести объявлением структурированной привязки.
|
Количество идентификаторов в
sb-identifier-list
должно быть равно размеру структурированной привязки
|
(до C++26) |
|
Пусть количество идентификаторов в
sb-identifier-list
равно
N
, а размер структурированной привязки
|
(начиная с 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
, а не к введенным структурированным привязкам:
Кортежеподобная интерпретация всегда используется, если
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) | Структурированные привязки могут вводить пакет |
Ключевые слова
Пример
#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 или распаковывает кортеж в отдельные объекты
(шаблон функции) |