ядро языка — синтаксисcore language — syntax
Разбор tuple, pair, массива или struct на именованные переменные одной строкой — auto [a, b] = pair; вместо .first/.second или std::tie.Unpacking a tuple, pair, array, or struct into named variables in one line — auto [a, b] = pair; instead of .first/.second or std::tie.
Structured bindings — синтаксис объявления нескольких именованных переменных сразу из одного составного объекта: std::pair, std::tuple, обычного массива фиксированного размера, или struct/class с исключительно публичными нестатическими членами. Форма auto [a, b, c] = expr; вводит a, b, c как имена, ссылающиеся на элементы expr — по одному имени на элемент, в порядке их объявления.Structured bindings are syntax for declaring several named variables at once from a single compound object: std::pair, std::tuple, a fixed-size raw array, or a struct/class with exclusively public non-static members. The form auto [a, b, c] = expr; introduces a, b, c as names referring to the elements of expr — one name per element, in declaration order.
std::map<std::string, int>::iterator it = m.find("key");
if (it != m.end()) {
const auto& key = it->first;
int value = it->second;
}
// или для функций, возвращающих tuple:// or for functions returning a tuple:
int a; double b; std::string c;
std::tie(a, b, c) = make_tuple(); // все переменные объявлены заранее// every variable declared ahead of time
auto it = m.find("key");
if (auto [key, value] = *it; it != m.end()) {
// key и value уже готовы, без ->first/->second// key and value are ready, no ->first/->second
}
auto [a, b, c] = make_tuple(); // одна строка, ничего не объявлено заранее// one line, nothing pre-declared
Составные возвращаемые значения были в языке давно, но всегда требовали многословного разбора:Compound return values had been in the language for a long time, but always required verbose unpacking:
До structured bindings единственный способ разложить std::tuple на именованные переменные одной операцией — std::tie(a, b, c) = expr; — но a, b, c должны существовать заранее, с явно указанными типами. Совместить это с auto-выводом типов было невозможно.Before structured bindings, the only way to unpack a std::tuple into named variables in one operation was std::tie(a, b, c) = expr; — but a, b, c had to already exist, with explicitly stated types. Combining this with auto type deduction was impossible.
Итерация по std::map через for (auto& p : m) { use(p.first, p.second); } не даёт коду сказать, что такое first и second семантически — имена участников цикла приходится восстанавливать по контексту, а не читать напрямую из кода.Iterating over a std::map via for (auto& p : m) { use(p.first, p.second); } gives the code no way to say what first and second mean semantically — readers have to reconstruct the meaning from context instead of reading it straight off the code.
Возврат нескольких значений через std::tuple был возможен и раньше, но разбор результата на стороне вызывающего кода был настолько многословным, что многие API предпочитали выходные параметры (указатели/ссылки) вместо честного multiple return — structured bindings делают tuple-возврат таким же удобным, как и объявление обычных переменных.Returning multiple values via std::tuple was already possible, but unpacking the result on the caller's side was so verbose that many APIs preferred output parameters (pointers/references) over an honest multiple return — structured bindings make a tuple return as convenient as declaring ordinary variables.
Структурная привязка не создаёт a, b, c как независимые переменные — она сперва создаёт единственную скрытую переменную (назовём её e), инициализированную тем же выражением, а a, b, c — это просто имена-псевдонимы для элементов e. Поведение зависит от вида объекта:A structured binding doesn't create a, b, c as independent variables — it first creates a single hidden variable (call it e) initialized with the same expression, and a, b, c are just alias names for the elements of e. The behavior depends on the kind of object:
// для std::pair/std::tuple — вызывает get<0>(e), get<1>(e), ...:// for std::pair/std::tuple — calls get<0>(e), get<1>(e), ...:
auto [a, b] = std::make_pair(1, "x");
// концептуально эквивалентно:// conceptually equivalent to:
auto e = std::make_pair(1, "x");
auto& a = std::get<0>(e);
auto& b = std::get<1>(e);
// для struct с публичными членами — привязка к членам по порядку объявления:// for a struct with public members — binds to members in declaration order:
struct Point { int x, y; };
auto [px, py] = Point{1, 2}; // px псевдоним e.x, py псевдоним e.y// px aliases e.x, py aliases e.y
// для массива фиксированного размера — привязка к элементам напрямую:// for a fixed-size array — binds to elements directly:
int arr[2] = {10, 20};
auto [first, second] = arr;
Ключевое следствие скрытой переменной e: const auto& [a, b] = expr; продлевает время жизни временного объекта ровно так же, как обычное const auto& x = expr; — привязка к rvalue безопасна, пока жива скрытая e. Изменяемость тоже наследуется от e: auto [a, b] = pair; копирует, auto& [a, b] = pair; даёт изменяемые псевдонимы на исходный объект.The key consequence of the hidden e: const auto& [a, b] = expr; extends the lifetime of a temporary exactly the way an ordinary const auto& x = expr; would — binding to an rvalue is safe as long as the hidden e is alive. Mutability is likewise inherited from e: auto [a, b] = pair; copies, auto& [a, b] = pair; gives mutable aliases to the original object.
До C++20 имена из structured binding нельзя было напрямую захватить в лямбде по значению ([a]{ use(a); } не компилировался в C++17, потому что a — не сама переменная, а привязка к члену скрытой e). C++20 разрешил это ограничение, но на C++17 компиляторе такой код всё ещё не соберётся — частая причина ошибки компиляции при попытке "поймать" один из элементов structured binding в замыкание.Before C++20, names from a structured binding couldn't be captured by value directly in a lambda ([a]{ use(a); } failed to compile in C++17, because a isn't the variable itself but a binding to a member of the hidden e). C++20 lifted this restriction, but on a C++17 compiler such code still won't build — a common source of compile errors when trying to "catch" one of the structured binding's elements in a closure.
auto [x, y] = point; всегда привязывает x к первому объявленному члену struct и y — ко второму, независимо от того, как называются сами члены. Если порядок объявления членов struct поменяется при рефакторинге, смысл x/y в существующем коде молча изменится — компилятор об этом не предупредит.auto [x, y] = point; always binds x to the struct's first declared member and y to the second, regardless of what those members are actually named. If the struct's member declaration order changes during a refactor, the meaning of x/y in existing code silently changes — the compiler won't warn about it.
Structured bindings работают "из коробки" только для struct без приватных/защищённых нестатических членов, для массивов, и для типов со специализированными std::tuple_size/std::tuple_element/get<N> (tuple-like протокол). Для произвольного класса с приватными полями и геттерами нужно либо открыть поля, либо специализировать tuple-протокол вручную.Structured bindings work out of the box only for a struct with no private/protected non-static members, for arrays, and for types with specialized std::tuple_size/std::tuple_element/get<N> (the tuple-like protocol). For an arbitrary class with private fields and getters, you either need to make the fields public or specialize the tuple protocol by hand.