Menu
Coddy logo textTech

構造化束縛

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 88/104。

C++17で導入された構造化束縛 (Structured bindings)を使用すると、配列、ペア、タプル、または構造体から複数の値を、1つの宣言で個別の名前付き変数に展開することができます。これにより、複合データを扱う際のコードがより簡潔で読みやすくなります。

構文では、新しい変数名を含む角括弧とともに auto を使用します:

#include <iostream>
#include <map>
#include <tuple>

int main() {
    // pairのアンパック
    std::pair<std::string, int> person{"Alice", 25};
    auto [name, age] = person;
    std::cout << name << " is " << age << "\n";
    
    // tupleのアンパック
    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)の威力を示す、学生の成績アナライザーを構築しましょう。学生の記録とコースデータを処理するシステムを作成し、コード全体で構造化束縛を使用して、ペア(pair)、タプル(tuple)、および構造体(struct)をエレガントに展開します。

コードは以下の3つのファイルに分けて構成します:

  • Student.h: 学生の名前(std::string)、ID(int)、およびGPA(double)のパブリックメンバを持つ Student 構造体を定義します。また、名前、ID、GPAを受け取り、Student 構造体を返す createStudent という関数を作成します。
  • GradeUtils.h: 成績処理のためのユーティリティ関数を作成します。

    数値のスコア(整数)を受け取り、3つの値(文字による成績(std::string)、グレードポイント(double)、および合格かどうか(bool))を含む std::tuple を返す関数 getGradeInfo を定義します。以下の成績スケールを使用してください:

    • 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

    また、コース名(std::string)とスコア(int)を受け取り、それらの値の std::pair を返す関数 makeScorePair を定義します。

  • main.cpp: 以下の4つの入力を読み取ります:
    1. 学生の名前(文字列)
    2. 学生ID(整数)
    3. コース名(文字列)
    4. スコア(整数)

    main関数全体で構造化束縛を使用してください:

    1. 名前、ID、およびプレースホルダーとしてのGPA 0.0 を指定して createStudent を呼び出します。構造化束縛を使用して、返された構造体を個別の変数に展開し、次のように出力します:Student: [name] (ID: [id])
    2. コース名とスコアを指定して makeScorePair を呼び出します。構造化束縛を使用してペアを展開し、次のように出力します:Course: [course], Score: [score]
    3. スコアを指定して getGradeInfo を呼び出します。構造化束縛を使用してタプルの3つの要素すべてを展開し、次のように出力します:
      Grade: [letter]
      Points: [points]
      Passing: [yes/no]
      (合格が true の場合は "yes"、false の場合は "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)を使用すると、配列、ペア、タプル、または構造体から複数の値を、1つの宣言で個別の名前付き変数に展開できます。

基本的な構文では、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;

std::mapのようなコンテナを使用した範囲ベースの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}; }

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: name、id、およびプレースホルダーの GPA 0.0 を指定して createStudent を呼び出す
    // 構造化束縛を使用して構造体をアンパックする: auto [name, id, gpa] = ...
    // 出力: Student: [name] (ID: [id])
    
    
    // TODO: courseName と score を指定して makeScorePair を呼び出す
    // 構造化束縛を使用してペアをアンパックする: auto [course, sc] = ...
    // 出力: Course: [course], Score: [score]
    
    
    // TODO: score を指定して getGradeInfo を呼び出す
    // 構造化束縛を使用してタプルをアンパックする: auto [letter, points, passing] = ...
    // 出力:
    // Grade: [letter]
    // Points: [points]
    // Passing: [yes/no] (passing が true の場合は "yes"、false の場合は "no" を出力する)
    
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン