QMap::begin()だけじゃない!Qtでマップを操作する代替メソッド

2025-05-27

QMapは、Qtにおけるキーと値のペアを格納する連想コンテナ(マップ)です。std::mapに似ていますが、Qt独自の機能やパフォーマンス最適化が施されています。

QMap::begin()メソッドは、このQMapコンテナの最初の要素を指すイテレータを返します。イテレータは、コンテナ内の要素を順番に走査(アクセス)するためのオブジェクトです。

具体的な機能

  • イテレータの型
    • iterator QMap::begin(): 要素の値を変更可能なイテレータ(非const版)を返します。
    • const_iterator QMap::begin() const: 要素の値を変更できないイテレータ(const版)を返します。通常、マップの内容を変更する必要がない場合にこれを使用します。
  • 空のマップの場合
    もしQMapが空の場合、begin()が返すイテレータはend()が返すイテレータと等しくなります。これは、コンテナに要素がないことを示す一般的なパターンです。
  • イテレーションの開始点
    QMapの要素をループ処理などで順に処理する際に、このbegin()がイテレーションの開始点となります。
  • 最初の要素へのアクセス
    begin()が返すイテレータは、マップ内でキーによってソートされた順序での最初のキーと値のペアを指します。

使用例

QMapのすべての要素を順番に処理する一般的な方法は、begin()end()イテレータを使用するループです。

#include <QMap>
#include <QDebug> // デバッグ出力用

int main() {
    QMap<QString, int> map;
    map.insert("apple", 100);
    map.insert("banana", 50);
    map.insert("cherry", 200);

    // STLスタイルのイテレータを使ってQMapの要素を走査
    // map.begin() は最初の要素("apple", 100)を指すイテレータを返す
    QMap<QString, int>::iterator i = map.begin();
    while (i != map.end()) { // i が map.end() に達するまで繰り返す
        qDebug() << "Key:" << i.key() << ", Value:" << i.value();
        ++i; // 次の要素へ進む
    }

    // C++11以降の範囲ベースforループを使う場合 (より簡潔)
    // この場合、QMap::begin()とQMap::end()が内部的に呼ばれる
    qDebug() << "\n--- Range-based for loop ---";
    for (auto it = map.begin(); it != map.end(); ++it) {
        qDebug() << "Key:" << it.key() << ", Value:" << it.value();
    }
    
    // 値のみにアクセスしたい場合 (注意: キーには直接アクセスできない)
    qDebug() << "\n--- Range-based for loop (values only) ---";
    for (int value : map) { // QMapは直接値をイテレートすることも可能
        qDebug() << "Value:" << value;
    }

    return 0;
}
Key: "apple" , Value: 100
Key: "banana" , Value: 50
Key: "cherry" , Value: 200

--- Range-based for loop ---
Key: "apple" , Value: 100
Key: "banana" , Value: 50
Key: "cherry" , Value: 200

--- Range-based for loop (values only) ---
Value: 100
Value: 50
Value: 200
  • QMapは「Implicitly Shared(暗黙的に共有)」という特性を持っています。これは、QMapをコピーしても、実際にデータがコピーされるのは、どちらかのコピーが変更された時(copy-on-write)であるため、begin()のような操作は非常に高速です。
  • QMapにはJavaスタイルのイテレータ(QMapIteratorQMutableMapIterator)も提供されており、それらを使用することも可能です。
  • QMapはキーによって要素がソートされるため、begin()が返すイテレータは常に最も小さいキーを持つ要素を指します。


QMap::begin()自体は比較的シンプルなメソッドですが、イテレータの扱い方やQMapの状態によって様々な問題が発生する可能性があります。

無効なイテレータのデリファレンス (Dereferencing an invalid iterator)

これは最も一般的なエラーの一つです。begin()が返すイテレータを正しく扱わないと、未定義動作やクラッシュを引き起こす可能性があります。

エラーの状況

  • イテレータが無効になった後にデリファレンスする
    QMapの内容をイテレーション中に変更(要素の追加、削除など)すると、既存のイテレータが無効になることがあります。無効になったイテレータをデリファレンスすると、未定義動作やクラッシュにつながります。
    QMap<QString, int> myMap;
    myMap.insert("a", 1);
    myMap.insert("b", 2);
    
    QMap<QString, int>::iterator it = myMap.begin(); // "a" を指す
    
    myMap.remove("a"); // ここで 'it' が無効になる可能性がある
    
    qDebug() << it.key(); // 無効なイテレータをデリファレンスしようとしてクラッシュ
    
  • 空のマップでイテレータをデリファレンスする
    QMapが空の場合、begin()end()と同じイテレータを返します。このイテレータを*iのようにデリファレンスしようとすると、クラッシュ(セグメンテーション違反など)が発生します。
    QMap<QString, int> myMap;
    // ... myMap.insert() が呼ばれない場合
    
    QMap<QString, int>::iterator it = myMap.begin();
    // if (it == myMap.end()) のチェックを忘れると...
    qDebug() << it.key(); // ここでクラッシュする可能性がある
    

トラブルシューティング

  • イテレーション中のマップ変更を避けるか、安全な方法で行う
    • イテレーション中に要素を削除する必要がある場合は、削除後に次の有効なイテレータを返すerase()メソッドを使用します。
      QMap<QString, int> myMap;
      myMap.insert("a", 1);
      myMap.insert("b", 2);
      myMap.insert("c", 3);
      
      QMap<QString, int>::iterator it = myMap.begin();
      while (it != myMap.end()) {
          if (it.value() == 2) {
              it = myMap.erase(it); // eraseは次の有効なイテレータを返す
          } else {
              ++it;
          }
      }
      
    • C++11以降の範囲ベースforループは、イテレーション中のコンテナ変更には適していません。変更が必要な場合は、明示的なイテレータを使ったループを使用するか、一度別のコンテナにコピーしてから処理を検討してください。
  • 常にbegin()とend()の比較を行う
    ループでイテレータを使用する際は、必ずit != map.end()のような条件でループを継続するかどうかをチェックしてください。
    QMap<QString, int> myMap;
    // ...
    
    for (QMap<QString, int>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
        qDebug() << "Key:" << it.key() << ", Value:" << it.value();
    }
    

constと非constイテレータの不一致

エラーの状況

  • 逆に、非constQMapconst_iteratorを使用するのは問題ありませんが、そのイテレータで要素の値を変更することはできません。
  • constQMapオブジェクトに対して、非constなイテレータを使用しようとするとコンパイルエラーになります。
    void printMap(const QMap<QString, int>& map) {
        // mapはconstなので、begin()はconst_iteratorを返す
        QMap<QString, int>::iterator it = map.begin(); // コンパイルエラー!
        // QMap<QString, int>::const_iterator it = map.begin(); // これが正しい
    }
    

トラブルシューティング

  • autoキーワードの利用
    C++11以降ではautoキーワードを使うことで、コンパイラが自動的に正しいイテレータ型を推論してくれるため、この種のエラーを減らすことができます。
    // 非constなマップ
    QMap<QString, int> myMap;
    // ...
    for (auto it = myMap.begin(); it != myMap.end(); ++it) {
        // 'it' は QMap<QString, int>::iterator と推論される
        it.value() = 10; // 値の変更が可能
    }
    
    // constなマップ
    const QMap<QString, int> constMap = myMap;
    for (auto it = constMap.begin(); it != constMap.end(); ++it) {
        // 'it' は QMap<QString, int>::const_iterator と推論される
        // it.value() = 10; // コンパイルエラー!(const_iteratorなので変更不可)
    }
    
  • 適切なイテレータ型を使用する
    constなオブジェクトを扱う場合はconst_iteratorを使用します。要素を変更する必要がある場合は、非constなオブジェクトとiteratorを使用します。

イテレータの比較に関する誤解

エラーの状況

  • 異なるQMapオブジェクトのイテレータ同士を比較しようとする。これは未定義動作です。
    QMap<QString, int> map1;
    map1.insert("a", 1);
    QMap<QString, int> map2;
    map2.insert("b", 2);
    
    QMap<QString, int>::iterator it1 = map1.begin();
    QMap<QString, int>::iterator it2 = map2.begin();
    
    if (it1 == it2) { // これは意味がなく、未定義動作
        // ...
    }
    

トラブルシューティング

  • イテレータは、それが属する単一のコンテナ内でのみ意味を持ちます。異なるコンテナのイテレータを比較することはできません。

QMapのライフタイム問題 (Dangling Pointers/References)

begin()自体が直接的な原因となることは稀ですが、イテレータが指すQMapオブジェクト自体がスコープ外に出てしまったり、不正に破棄されたりすると、イテレータが「ぶら下がりポインタ」となり、クラッシュします。

エラーの状況

QMap<QString, int>::iterator globalIterator; // グローバル変数やクラスメンバー

void createMapAndSetIterator() {
    QMap<QString, int> localMap; // ローカル変数
    localMap.insert("test", 123);
    globalIterator = localMap.begin(); // localMapが破棄された後、globalIteratorは無効になる
} // localMap はここで破棄される

void useIterator() {
    // createMapAndSetIterator() が実行された後、globalIteratorは無効
    qDebug() << globalIterator.key(); // クラッシュする可能性が高い
}

トラブルシューティング

  • 特に複雑なオブジェクトのポインタや参照をQMapの値として格納する場合、それらのオブジェクト自体のライフタイム管理に注意が必要です。QSharedPointerなどのスマートポインタの使用も有効です。
  • 通常、イテレータは一時的なループ処理のためにのみ使用し、長期的に保持する必要がある場合は、キーや値自体をコピーして保持することを検討します。
  • イテレータのライフタイムが、それが参照するQMapオブジェクトのライフタイムを超えないように設計します。

予期せぬソート順序

QMapはキーによって要素をソートします。begin()は常にソート順で最初の要素を返しますが、キーの型によっては、予期しないソート順になることがあります。

エラーの状況

  • 数値のキーをQStringとして扱う場合など、文字列のソート順が数値のソート順と異なるため、begin()が返す要素が期待と異なる場合があります。
    QMap<QString, int> numbersMap;
    numbersMap.insert("10", 100);
    numbersMap.insert("2", 200);
    numbersMap.insert("1", 300);
    
    // 文字列としてソートされるため、"1" -> "10" -> "2" の順になる
    // begin() は "1" を指す
    QMap<QString, int>::iterator it = numbersMap.begin();
    qDebug() << "First element key:" << it.key(); // 出力: "1"
    // 期待: "1" または "2" (数値として最小)
    
  • カスタムクラスをキーとして使用する場合は、operator<を適切にオーバーロードする必要があります。
  • 数値であれば数値型(int, doubleなど)をキーに使い、文字列であればQStringをキーに使います。
  • QMapのキーは、<演算子(またはカスタム比較演算子)によってソートされます。キーの型が正しくソートされることを確認してください。


QMapの全要素を走査する基本的なループ

これは最も一般的なQMap::begin()の使用例です。マップのすべてのキーと値のペアを順番に処理します。

#include <QCoreApplication> // QCoreApplicationのために必要 (コンソールアプリケーションの場合)
#include <QMap>
#include <QDebug> // qDebug() のために必要

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv); // コンソールアプリケーションの初期化

    QMap<QString, int> studentScores; // 学生の名前と点数を格納するQMap
    studentScores.insert("Alice", 95);
    studentScores.insert("Bob", 88);
    studentScores.insert("Charlie", 72);
    studentScores.insert("David", 91);

    qDebug() << "--- 学生の成績リスト (STLスタイルイテレータ) ---";

    // QMap::iterator を使用して走査
    // begin() は最初の要素を指すイテレータを返す
    // end() は最後の要素の次を指す「番兵」イテレータを返す
    QMap<QString, int>::iterator it = studentScores.begin();
    while (it != studentScores.end()) {
        qDebug() << "名前: " << it.key() << ", 点数: " << it.value();
        ++it; // 次の要素へ進む
    }

    return 0; // QCoreApplicationを使わない場合は return 0;
              // QCoreApplicationを使う場合は a.exec() が必要だが、
              // ここではイテレーションのみなので省略可能
}

実行結果

--- 学生の成績リスト (STLスタイルイテレータ) ---
名前: "Alice" , 点数: 95
名前: "Bob" , 点数: 88
名前: "Charlie" , 点数: 72
名前: "David" , 点数: 91

解説

  • ++it;: イテレータを次の要素に進めます。
  • it.value(): 現在のイテレータが指す要素の値を取得します。
  • it.key(): 現在のイテレータが指す要素のキーを取得します。
  • while (it != studentScores.end()): イテレータitend()(マップの末尾の「番兵」)に達するまでループを続けます。
  • QMap<QString, int>::iterator it = studentScores.begin();: studentScoresの最初の要素を指すイテレータitを初期化します。QMapはキーでソートされるため、アルファベット順(この場合は文字列の比較順)で最初のキーを持つ要素が最初の要素になります。

C++11 以降の範囲ベースforループ (QMap::begin() / end()の暗黙的使用)

C++11以降では、より簡潔な範囲ベースforループを使用してQMapを走査できます。この場合、コンパイラが内部的にQMap::begin()QMap::end()を呼び出します。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> productPrices; // 製品名と価格
    productPrices.insert("Laptop", 120000);
    productPrices.insert("Mouse", 3500);
    productPrices.insert("Keyboard", 8000);

    qDebug() << "--- 製品価格リスト (範囲ベースforループ) ---";

    // QMap の要素を自動的にイテレート
    // auto it は QMap<QString, int>::iterator または const_iterator に推論される
    for (auto it = productPrices.begin(); it != productPrices.end(); ++it) {
        qDebug() << "製品: " << it.key() << ", 価格: " << it.value();
    }
    
    // QMap は値のみを直接イテレートすることもできる (C++11の範囲ベースforループの特殊なケース)
    qDebug() << "\n--- 製品価格リスト (値のみ) ---";
    for (int price : productPrices) {
        qDebug() << "価格 (値のみ): " << price;
    }

    return 0;
}

実行結果

--- 製品価格リスト (範囲ベースforループ) ---
製品: "Keyboard" , 価格: 8000
製品: "Laptop" , 価格: 120000
製品: "Mouse" , 価格: 3500

--- 製品価格リスト (値のみ) ---
価格 (値のみ): 8000
価格 (値のみ): 120000
価格 (値のみ): 3500

解説

  • for (int price : productPrices): QMapは、範囲ベースforループで直接値にアクセスできる便利な機能を提供します。この場合、キーには直接アクセスできません。
  • for (auto it = productPrices.begin(); it != productPrices.end(); ++it): これは上のSTLスタイルのループと同じことを行いますが、autoを使うことでイテレータの型宣言を省略できます。

イテレーション中に要素の値を変更する

QMap::iterator(非const版)を使用すると、イテレーション中に要素の値を変更できます。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> stockLevels; // 在庫レベル
    stockLevels.insert("Pen", 100);
    stockLevels.insert("Notebook", 50);
    stockLevels.insert("Eraser", 200);

    qDebug() << "--- 在庫レベル (変更前) ---";
    for (auto it = stockLevels.begin(); it != stockLevels.end(); ++it) {
        qDebug() << "商品: " << it.key() << ", 在庫: " << it.value();
    }

    qDebug() << "\n--- 在庫レベル (変更中: 全て+10) ---";
    // 非constイテレータなので、value()で参照を取得し、値を変更できる
    QMap<QString, int>::iterator it = stockLevels.begin();
    while (it != stockLevels.end()) {
        it.value() += 10; // 値を10増やす
        qDebug() << "商品: " << it.key() << ", 新在庫: " << it.value();
        ++it;
    }

    qDebug() << "\n--- 在庫レベル (変更後: 再確認) ---";
    for (auto it = stockLevels.begin(); it != stockLevels.end(); ++it) {
        qDebug() << "商品: " << it.key() << ", 在庫: " << it.value();
    }

    return 0;
}

実行結果

--- 在庫レベル (変更前) ---
商品: "Eraser" , 在庫: 200
商品: "Notebook" , 在庫: 50
商品: "Pen" , 在庫: 100

--- 在庫レベル (変更中: 全て+10) ---
商品: "Eraser" , 新在庫: 210
商品: "Notebook" , 新在庫: 60
商品: "Pen" , 新在庫: 110

--- 在庫レベル (変更後: 再確認) ---
商品: "Eraser" , 在庫: 210
商品: "Notebook" , 在庫: 60
商品: "Pen" , 在庫: 110

解説

  • it.value() += 10;: it.value()は値への参照を返すため、直接値を変更できます。

イテレーション中に要素を削除する場合は、QMap::erase()メソッドの戻り値を使用することが重要です。erase()は削除された要素の次の有効なイテレータを返します。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> items;
    items.insert("Apple", 5);
    items.insert("Banana", 0); // 削除対象
    items.insert("Cherry", 10);
    items.insert("Date", 0);   // 削除対象
    items.insert("Elderberry", 15);

    qDebug() << "--- 初期マップ ---";
    for (auto it = items.begin(); it != items.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    qDebug() << "\n--- ゼロ在庫のアイテムを削除 ---";
    QMap<QString, int>::iterator it = items.begin();
    while (it != items.end()) {
        if (it.value() == 0) {
            qDebug() << "削除中: " << it.key();
            it = items.erase(it); // 削除し、次の有効なイテレータを受け取る
        } else {
            ++it; // 削除しない場合は次の要素へ進む
        }
    }

    qDebug() << "\n--- 削除後のマップ ---";
    for (auto it = items.begin(); it != items.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    return 0;
}

実行結果

--- 初期マップ ---
"Apple" : 5
"Banana" : 0
"Cherry" : 10
"Date" : 0
"Elderberry" : 15

--- ゼロ在庫のアイテムを削除 ---
削除中: "Banana"
削除中: "Date"

--- 削除後のマップ ---
"Apple" : 5
"Cherry" : 10
"Elderberry" : 15
  • else { ++it; }: 削除しない場合は、手動でイテレータを進める必要があります。これを忘れると無限ループに陥る可能性があります。
  • it = items.erase(it);: 要素を削除すると、元のイテレータは無効になります。erase()メソッドは次の有効なイテレータを返すため、それをitに再代入することで安全にイテレーションを続行できます。


Javaスタイルのイテレータ (QMapIterator および QMutableMapIterator)

Qtは、Javaのイテレータパターンに似た独自のイテレータクラスを提供しています。これらはSTLスタイルのイテレータよりも高レベルで、使いやすいと感じる人もいるかもしれません。ただし、パフォーマンスはSTLスタイルより若干劣るとされています。

  • QMutableMapIterator<Key, T>: マップの内容をイテレーション中に変更(値の更新、要素の削除)する場合に使用します。
  • QMapIterator<Key, T>: マップの内容を変更せずに読み取り専用で走査する場合に使用します。

使用例 (QMapIterator)

#include <QCoreApplication>
#include <QMap>
#include <QMapIterator> // QMapIteratorのために必要
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> fruits;
    fruits.insert("Apple", 10);
    fruits.insert("Banana", 5);
    fruits.insert("Cherry", 12);

    qDebug() << "--- フルーツ在庫リスト (QMapIterator) ---";

    QMapIterator<QString, int> i(fruits); // QMapを引数にコンストラクタを呼び出す
    while (i.hasNext()) { // 次の要素があるかチェック
        i.next(); // イテレータを次の要素に進める
        qDebug() << "フルーツ: " << i.key() << ", 在庫: " << i.value();
    }

    return 0;
}

使用例 (QMutableMapIterator - 変更と削除)

#include <QCoreApplication>
#include <QMap>
#include <QMutableMapIterator> // QMutableMapIteratorのために必要
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> items;
    items.insert("Book", 25);
    items.insert("Pen", 0);
    items.insert("Notebook", 15);
    items.insert("Eraser", 0);

    qDebug() << "--- アイテム在庫リスト (変更前) ---";
    for (auto it = items.begin(); it != items.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    qDebug() << "\n--- 在庫0のアイテムを削除し、それ以外の在庫を増やす (QMutableMapIterator) ---";
    QMutableMapIterator<QString, int> i(items);
    while (i.hasNext()) {
        i.next();
        if (i.value() == 0) {
            qDebug() << "削除: " << i.key();
            i.remove(); // 現在の要素を削除
        } else {
            i.setValue(i.value() + 5); // 値を更新
            qDebug() << "更新: " << i.key() << " -> " << i.value();
        }
    }

    qDebug() << "\n--- 変更後のアイテム在庫リスト ---";
    for (auto it = items.begin(); it != items.end(); ++it) {
        qDebug() << it.key() << ":" << it.value();
    }

    return 0;
}

Javaスタイルイテレータの主な特徴

  • remove() (QMutableMapIteratorのみ): 現在の要素を削除します。
  • setValue() (QMutableMapIteratorのみ): 現在の要素の値を変更します。
  • key() / value(): 現在の要素のキーと値を取得します。
  • next() / previous(): イテレータを移動させ、その要素を返します。
  • hasNext() / hasPrevious(): 前後への移動が可能かチェックします。

キーリスト (keys()) と値リスト (values()) を使用する

QMapは、すべてのキーまたはすべての値をそれぞれQListとして取得する便利なメソッドを提供します。これにより、特定の目的のためにマップ全体をイテレートすることなく、キーや値のみを処理できます。

  • QList<T> QMap::values() const: マップ内のすべての値を、対応するキーの昇順で並べられたQListとして返します。
  • QList<Key> QMap::keys() const: マップ内のすべてのキーを昇順にソートされたQListとして返します。

使用例 (keys() と values())

#include <QCoreApplication>
#include <QMap>
#include <QList>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, QString> capitals;
    capitals.insert("Japan", "Tokyo");
    capitals.insert("USA", "Washington D.C.");
    capitals.insert("France", "Paris");
    capitals.insert("Germany", "Berlin");

    qDebug() << "--- すべての国名 (キー) ---";
    QList<QString> countryKeys = capitals.keys();
    for (const QString &key : countryKeys) {
        qDebug() << key;
    }

    qDebug() << "\n--- すべての首都名 (値) ---";
    QList<QString> capitalValues = capitals.values();
    for (const QString &value : capitalValues) {
        qDebug() << value;
    }

    qDebug() << "\n--- キーリストを元にキーと値をペアで表示 ---";
    for (const QString &key : capitals.keys()) {
        qDebug() << "国: " << key << ", 首都: " << capitals.value(key); // または capitals[key]
    }

    return 0;
}

実行結果

--- すべての国名 (キー) ---
"France"
"Germany"
"Japan"
"USA"

--- すべての首都名 (値) ---
"Paris"
"Berlin"
"Tokyo"
"Washington D.C."

--- キーリストを元にキーと値をペアで表示 ---
国: "France" , 首都: "Paris"
国: "Germany" , 首都: "Berlin"
国: "Japan" , 首都: "Tokyo"
国: "USA" , 首都: "Washington D.C."

解説

  • この方法は、マップ全体をコピーしてQListを作成するため、大きなマップではパフォーマンスに影響を与える可能性があります。しかし、単純な走査や、キーまたは値のみが必要な場合には非常に便利です。

Qt 4.x時代に広く使われていたforeachマクロは、C++11の範囲ベースforループがない時代に、コンテナの走査を簡潔に行うためのQt独自の機能でした。現在ではC++11の範囲ベースforループの使用が推奨されており、Qt 5.7以降では非推奨となっています。

使用例 (foreach)

#include <QCoreApplication>
#include <QMap>
#include <QDebug>

// Qt 5.7以降では CONFIG += no_keywords を .pro に追加すると foreach が無効になることがある
// その場合、この例はコンパイルエラーになる可能性があります。

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QMap<QString, int> scores;
    scores.insert("Math", 85);
    scores.insert("Science", 90);
    scores.insert("History", 78);

    qDebug() << "--- 科目の点数 (foreach マクロ) ---";

    // foreach は QMap の値を直接イテレートします
    // キーには直接アクセスできません。
    foreach (int score, scores) {
        qDebug() << "点数: " << score;
    }

    // キーと値の両方にアクセスしたい場合は、QMap::keys() を組み合わせて使用
    qDebug() << "\n--- 科目の点数 (foreach + keys()) ---";
    foreach (const QString &subject, scores.keys()) {
        qDebug() << "科目: " << subject << ", 点数: " << scores.value(subject);
    }

    return 0;
}

実行結果

--- 科目の点数 (foreach マクロ) ---
点数: 78
点数: 85
点数: 90

--- 科目の点数 (foreach + keys()) ---
科目: "History" , 点数: 78
科目: "Math" , 点数: 85
科目: "Science" , 点数: 90
  • 注意点: foreachマクロは、コンテナのコピーをイテレートするため、ループ内で元のコンテナを変更してもイテレーションには影響しません。しかし、これにより不要なコピーが発生し、パフォーマンスが低下する可能性があります。現代のC++では、C++11の範囲ベースforループ(上記「STLスタイルのイテレータ」の2番目の例)を推奨します。
  • foreach (const QString &subject, scores.keys()): keys()でキーのリストを取得し、そのリストをforeachでイテレートします。キーを使って元のマップから値を取得します。
  • foreach (int score, scores): QMapの値のみをイテレートします。
  • foreachマクロ (非推奨): 古いコードベースでよく見られる。C++11の範囲ベースforループに置き換えるべき。
  • keys() / values(): キーや値のリストを個別に取得したい場合に便利。マップ全体のコピーが発生するため、大きなマップでは注意が必要。
  • QMapIterator / QMutableMapIterator (Javaスタイルイテレータ): より高レベルなAPIを提供し、使いやすいと感じる場合がある。ただし、パフォーマンスは若干劣る。イテレーション中の変更にも対応。
  • QMap::begin() / end() (STLスタイルイテレータ): 最も一般的で効率的な方法。ループ内で要素の削除や変更が必要な場合に特に有効。