Структурированные привязки
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 88 из 104.
Структурированные привязки, представленные в C++17, позволяют распаковывать несколько значений из массивов, пар, кортежей или структур в отдельные именованные переменные в одном объявлении. Это делает код чище и читабельнее при работе с составными данными.
Синтаксис использует auto с квадратными скобками, содержащими имена новых переменных:
#include <iostream>
#include <map>
#include <tuple>
int main() {
// Распаковка пары
std::pair<std::string, int> person{"Alice", 25};
auto [name, age] = person;
std::cout << name << " is " << age << "\n";
// Распаковка кортежа
std::tuple<int, double, char> data{42, 3.14, 'X'};
auto [num, pi, letter] = data;
// Распаковка в циклах for на основе диапазона
std::map<std::string, int> scores{{"Bob", 90}, {"Carol", 85}};
for (const auto& [student, score] : scores) {
std::cout << student << ": " << score << "\n";
}
}Структурированные привязки также работают с массивами и пользовательскими структурами с открытыми полями:
struct Point {
double x;
double y;
};
Point getOrigin() { return {0.0, 0.0}; }
int main() {
auto [x, y] = getOrigin();
int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;
}Вы можете использовать auto& или const auto& для привязки по ссылке, избегая копирования и позволяя изменять исходные значения при необходимости. Эта возможность особенно полезна при итерации по контейнерам STL или обработке функций, возвращающих несколько значений.
Задание
ЛегкоДавайте создадим анализатор оценок студентов, который продемонстрирует мощь структурированных привязок (structured bindings). Вы создадите систему, которая обрабатывает записи о студентах и данные о курсах, используя структурированные привязки для элегантной распаковки пар, кортежей и структур во всем коде.
Вы организуете свой код в трех файлах:
Student.h: Определите структуруStudentс публичными членами для имени студента (std::string), ID (int) и GPA (double). Также создайте функциюcreateStudent, которая принимает имя, ID и GPA и возвращает структуруStudent.GradeUtils.h: Создайте вспомогательные функции для обработки оценок.Определите функцию
getGradeInfo, которая принимает числовой балл (целое число) и возвращаетstd::tuple, содержащий три значения: буквенную оценку (std::string), баллы за оценку (double) и информацию о том, является ли она проходной (bool). Используйте следующую шкалу оценивания:- 90+ : "A", 4.0, true
- 80-89: "B", 3.0, true
- 70-79: "C", 2.0, true
- 60-69: "D", 1.0, true
- Ниже 60: "F", 0.0, false
Также определите функцию
makeScorePair, которая принимает название курса (std::string) и балл (int), возвращаяstd::pairиз этих значений.main.cpp: Считайте четыре входных значения:- Имя студента (строка)
- ID студента (целое число)
- Название курса (строка)
- Балл (целое число)
Используйте структурированные привязки во всей функции main:
- Вызовите
createStudentс именем, ID и временным значением GPA 0.0. Используйте структурированные привязки, чтобы распаковать возвращенную структуру в отдельные переменные, затем выведите:Student: [name] (ID: [id]) - Вызовите
makeScorePairс названием курса и баллом. Используйте структурированные привязки для распаковки пары, затем выведите:Course: [course], Score: [score] - Вызовите
getGradeInfoс баллом. Используйте структурированные привязки для распаковки всех трех элементов кортежа, затем выведите:(Выведите "yes", если passing равно true, и "no", если false)Grade: [letter] Points: [points] Passing: [yes/no]
Например, при входных данных Alice, 1001, Math и 85:
Student: Alice (ID: 1001)
Course: Math, Score: 85
Grade: B
Points: 3
Passing: yesПри входных данных Bob, 2002, Physics и 55:
Student: Bob (ID: 2002)
Course: Physics, Score: 55
Grade: F
Points: 0
Passing: noНе забудьте подключить <tuple> и <utility> там, где это необходимо. Ключевая идея заключается в том, как структурированные привязки позволяют распаковывать составные типы — пары, кортежи и структуры — в именованные переменные в одном читаемом объявлении.
Шпаргалка
Структурные привязки (C++17) позволяют распаковывать несколько значений из массивов, пар, кортежей или структур в отдельные именованные переменные в одном объявлении.
Базовый синтаксис использует auto с квадратными скобками:
auto [var1, var2, ...] = composite_object;Распаковка std::pair:
std::pair<std::string, int> person{"Alice", 25};
auto [name, age] = person;Распаковка std::tuple:
std::tuple<int, double, char> data{42, 3.14, 'X'};
auto [num, pi, letter] = data;Использование структурных привязок в циклах for на основе диапазона с контейнерами, такими как std::map:
std::map<std::string, int> scores{{"Bob", 90}, {"Carol", 85}};
for (const auto& [student, score] : scores) {
std::cout << student << ": " << score << "\n";
}Распаковка структур с публичными членами:
struct Point {
double x;
double y;
};
Point getOrigin() { return {0.0, 0.0}; }
auto [x, y] = getOrigin();Распаковка массивов:
int arr[3] = {1, 2, 3};
auto [a, b, c] = arr;Используйте auto& или const auto& для привязки по ссылке, чтобы избежать копирования и разрешить изменение исходных значений:
auto& [x, y] = point; // Можно изменить оригинал
const auto& [name, age] = person; // Ссылка только для чтенияПопробуйте сами
#include <iostream>
#include <string>
#include "Student.h"
#include "GradeUtils.h"
int main() {
// Чтение входных данных
std::string studentName;
int studentId;
std::string courseName;
int score;
std::cin >> studentName;
std::cin >> studentId;
std::cin >> courseName;
std::cin >> score;
// TODO: Вызовите createStudent с name, id и заполнителем GPA, равным 0.0
// Используйте structured bindings для распаковки структуры: auto [name, id, gpa] = ...
// Выведите: Student: [name] (ID: [id])
// TODO: Вызовите makeScorePair с названием курса и score
// Используйте structured bindings для распаковки пары: auto [course, sc] = ...
// Выведите: Course: [course], Score: [score]
// TODO: Вызовите getGradeInfo с score
// Используйте structured bindings для распаковки кортежа: auto [letter, points, passing] = ...
// Выведите:
// Grade: [letter]
// Points: [points]
// Passing: [yes/no] (выведите "yes", если passing равно true, и "no", если false)
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер12Возможности современного C++
Семантика перемещения и RvaluesИдеальная передача (Perfect Forwarding)Лямбда-выражения в деталяхstd::function и std::bindconstexpr и constevalСтруктурированные привязкиoptional, variant, any