Menu
Coddy logo textTech

Структурированные привязки

Часть раздела Объектно-ориентированное программирование путешествия по 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 или обработке функций, возвращающих несколько значений.

challenge icon

Задание

Легко

Давайте создадим анализатор оценок студентов, который продемонстрирует мощь структурированных привязок (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: Считайте четыре входных значения:
    1. Имя студента (строка)
    2. ID студента (целое число)
    3. Название курса (строка)
    4. Балл (целое число)

    Используйте структурированные привязки во всей функции main:

    1. Вызовите createStudent с именем, ID и временным значением GPA 0.0. Используйте структурированные привязки, чтобы распаковать возвращенную структуру в отдельные переменные, затем выведите: Student: [name] (ID: [id])
    2. Вызовите makeScorePair с названием курса и баллом. Используйте структурированные привязки для распаковки пары, затем выведите: Course: [course], Score: [score]
    3. Вызовите getGradeInfo с баллом. Используйте структурированные привязки для распаковки всех трех элементов кортежа, затем выведите:
      Grade: [letter]
      Points: [points]
      Passing: [yes/no]
      (Выведите "yes", если passing равно true, и "no", если false)

Например, при входных данных 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование