構造化束縛
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コンテナを反復処理する場合や、複数の値を返す関数を処理する場合に特に便利です。
チャレンジ
簡単構造化束縛(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つの入力を読み取ります:- 学生の名前(文字列)
- 学生ID(整数)
- コース名(文字列)
- スコア(整数)
main関数全体で構造化束縛を使用してください:
- 名前、ID、およびプレースホルダーとしてのGPA 0.0 を指定して
createStudentを呼び出します。構造化束縛を使用して、返された構造体を個別の変数に展開し、次のように出力します:Student: [name] (ID: [id]) - コース名とスコアを指定して
makeScorePairを呼び出します。構造化束縛を使用してペアを展開し、次のように出力します:Course: [course], Score: [score] - スコアを指定して
getGradeInfoを呼び出します。構造化束縛を使用してタプルの3つの要素すべてを展開し、次のように出力します:(合格が true の場合は "yes"、false の場合は "no" を出力します)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)を使用すると、配列、ペア、タプル、または構造体から複数の値を、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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス