Qt QMap::erase()プログラミング例:実践で学ぶ効率的な要素削除

2025-05-27

これは、Qtのコンテナクラスである QMap から要素を削除するためのメンバ関数です。

QMap はキーと値のペアを格納する連想配列(マップ)であり、各キーはユニークです。erase() 関数は、指定されたイテレータが指す要素をマップから削除します。

関数のシグネチャ

template <typename Key, typename T>
class QMap {
public:
    // ... その他のメンバ ...
    iterator erase(iterator pos);
    // ... その他のメンバ ...
};
  • iterator: QMap の要素を指すイテレータの型です。
  • T: マップの値の型です。
  • Key: マップのキーの型です。

機能

この erase() 関数は、pos で指定されたイテレータが指す要素(キーと値のペア)を QMap から削除します。

戻り値

削除された要素の次の要素を指すイテレータを返します。これは、ループ内で要素を削除していく際に非常に便利です。std::maperase と同様の動作です。

使用例

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

int main() {
    QMap<QString, int> myMap;
    myMap.insert("apple", 10);
    myMap.insert("banana", 20);
    myMap.insert("cherry", 30);
    myMap.insert("date", 40);

    qDebug() << "Original Map:";
    for (auto it = myMap.begin(); it != myMap.end(); ++it) {
        qDebug() << "Key:" << it.key() << ", Value:" << it.value();
    }

    // "banana" を見つけて削除する
    auto it = myMap.find("banana");
    if (it != myMap.end()) {
        qDebug() << "\nErasing 'banana'...";
        it = myMap.erase(it); // 'it' は削除された要素の次の要素("cherry")を指すようになる
    }

    qDebug() << "\nMap after erasing 'banana':";
    for (auto it_map = myMap.begin(); it_map != myMap.end(); ++it_map) {
        qDebug() << "Key:" << it_map.key() << ", Value:" << it_map.value();
    }

    // 複数の要素をループしながら削除する例 (注意: イテレータの扱いが重要)
    qDebug() << "\nDemonstrating erasing multiple elements:";
    myMap.insert("elderberry", 50); // 要素を追加して削除テスト
    myMap.insert("fig", 60);

    qDebug() << "Map before multiple erase:";
    for (auto it_map = myMap.begin(); it_map != myMap.end(); ++it_map) {
        qDebug() << "Key:" << it_map.key() << ", Value:" << it_map.value();
    }

    auto current_it = myMap.begin();
    while (current_it != myMap.end()) {
        if (current_it.value() > 35) { // 値が35より大きいものを削除
            qDebug() << "Deleting:" << current_it.key();
            current_it = myMap.erase(current_it); // 削除後、次の要素を指すイテレータを受け取る
        } else {
            ++current_it; // 削除しない場合は、イテレータを進める
        }
    }

    qDebug() << "\nMap after multiple erase:";
    for (auto it_map = myMap.begin(); it_map != myMap.end(); ++it_map) {
        qDebug() << "Key:" << it_map.key() << ", Value:" << it_map.value();
    }

    return 0;
}
  • 代替手段
    • QMap::remove(const Key &key): キーを指定して要素を削除する場合、この関数の方が簡潔です。ただし、キーが複数ある場合(QMultiMapなど)、そのキーに関連するすべての要素を削除します。QMap ではキーはユニークなので、remove() は指定されたキーの要素を1つ削除します。erase(find(key)) とほぼ同じ意味になります。
    • QMap::clear(): マップ内のすべての要素を削除する場合に使用します。
  • 効率
    QMap は内部的に平衡二分探索木(通常は赤黒木)で実装されているため、要素の挿入、検索、削除は平均的に O(logn) の計算量で行われます。(n はマップ内の要素数)。
  • イテレータの無効化
    erase() が呼び出されると、削除された要素を指していたイテレータは無効になります。そのため、erase() の戻り値として返される「次の要素を指す有効なイテレータ」を使用することが非常に重要です。上記の例では、ループ内で it = myMap.erase(it); のようにして、イテレータを更新している点に注目してください。


QMap::erase(iterator pos) は非常に強力な関数ですが、イテレータの扱いを誤ると、クラッシュ(セグメンテーション違反など)や予期せぬ動作を引き起こす可能性があります。

イテレータの無効化(Invalidation)

これが最も一般的なエラーであり、最も重要な注意点です。 erase() 関数は、削除された要素を指していたイテレータを無効にします。無効になったイテレータにアクセスしようとすると、未定義動作となり、通常はクラッシュします。

誤った例

QMap<QString, int> myMap;
myMap.insert("A", 1);
myMap.insert("B", 2);
myMap.insert("C", 3);

for (auto it = myMap.begin(); it != myMap.end(); ++it) {
    if (it.key() == "B") {
        myMap.erase(it); // 'it' はここで無効になる
        // ++it; // ここで無効になったイテレータをインクリメントしようとしている!
    }
}
// このループはクラッシュする可能性が高い

トラブルシューティング/正しい使い方

erase() は、削除された要素の次の要素を指すイテレータを返します。この戻り値を利用して、イテレータを更新する必要があります。

QMap<QString, int> myMap;
myMap.insert("A", 1);
myMap.insert("B", 2);
myMap.insert("C", 3);
myMap.insert("D", 4);

auto it = myMap.begin();
while (it != myMap.end()) {
    if (it.key() == "B" || it.key() == "D") {
        qDebug() << "Erasing:" << it.key();
        it = myMap.erase(it); // ここでイテレータを更新する
    } else {
        ++it; // 削除しなかった場合は、手動でイテレータを進める
    }
}

qDebug() << "Map after erase:";
for (auto const& [key, value] : myMap.toStdMap()) { // C++17 structured binding
    qDebug() << "Key:" << key << ", Value:" << value;
}

QMapの特性
QMap は内部的に平衡二分探索木(通常は赤黒木)を使用しており、要素の削除はイテレータの無効化に対して比較的優しいです。削除された要素を指すイテレータのみが無効になり、他のイテレータは有効なままです(QVectorなどの配列ベースのコンテナでは、要素の削除によって後続のすべてのイテレータが無効になる場合があります)。ただし、上記のように erase() の戻り値を使うのが正しいイテレータの扱いです。

const_iterator を erase() に渡す

QMap::erase()QMap::iterator を引数に取ります。誤って QMap::const_iterator を渡そうとすると、コンパイルエラーになります。

誤った例

QMap<QString, int> myMap;
myMap.insert("A", 1);

for (QMap<QString, int>::const_iterator it = myMap.constBegin(); it != myMap.constEnd(); ++it) {
    // myMap.erase(it); // コンパイルエラー: cannot convert 'QMap<...>::const_iterator' to 'QMap<...>::iterator'
}

トラブルシューティング/正しい使い方

要素を削除する必要がある場合は、QMap::iterator を使用する必要があります。

QMap<QString, int> myMap;
myMap.insert("A", 1);

// QMap::begin() は QMap::iterator を返す
for (QMap<QString, int>::iterator it = myMap.begin(); it != myMap.end(); /* ++it はループ内で制御 */) {
    if (it.key() == "A") {
        it = myMap.erase(it);
    } else {
        ++it;
    }
}

あるいは、QMutableMapIterator を使用する方法もあります。

#include <QMutableMapIterator>

QMap<QString, int> myMap;
myMap.insert("A", 1);
myMap.insert("B", 2);

QMutableMapIterator<QString, int> i(myMap);
while (i.hasNext()) {
    i.next();
    if (i.key() == "B") {
        i.remove(); // QMutableMapIterator の remove() を使う
    }
}

QMutableMapIterator::remove() は、現在のアイテムをマップから削除し、イテレータは削除されたアイテムの次のアイテムを指すように自動的に調整されます。これにより、イテレータの無効化に関する問題を回避できます。

存在しないイテレータの削除

QMap::find() などで要素が見つからなかった場合 (QMap::end() を返す場合) に、そのイテレータを erase() に渡すと、未定義動作となります。

誤った例

QMap<QString, int> myMap;
myMap.insert("A", 1);

auto it = myMap.find("Z"); // "Z" は存在しないので it は myMap.end() を指す
// myMap.erase(it); // クラッシュする可能性が高い

トラブルシューティング/正しい使い方

find() の戻り値が end() でないことを常に確認してください。

QMap<QString, int> myMap;
myMap.insert("A", 1);

auto it = myMap.find("Z");
if (it != myMap.end()) { // 要素が見つかった場合のみ削除
    myMap.erase(it);
} else {
    qDebug() << "'Z' not found in map.";
}

ポインタが値として格納されている場合のメモリリーク

QMap に生のポインタ(SomeObject*)を値として格納している場合、erase() はマップからポインタを削除しますが、そのポインタが指すメモリは解放されません。これにより、メモリリークが発生します。

誤った例

QMap<QString, MyObject*> myMap;
myMap.insert("obj1", new MyObject());
myMap.insert("obj2", new MyObject());

// myMap.erase(myMap.find("obj1")); // ポインタは削除されるが、MyObject インスタンスはメモリに残る(リーク)

トラブルシューティング/正しい使い方

ポインタを削除する前に、それが指すオブジェクトを明示的に delete するか、スマートポインタ(QSharedPointer, QScopedPointer, std::unique_ptr など)を使用してください。

// 生ポインタの場合
QMap<QString, MyObject*> myMap;
myMap.insert("obj1", new MyObject());
myMap.insert("obj2", new MyObject());

auto it = myMap.find("obj1");
if (it != myMap.end()) {
    delete it.value(); // オブジェクトを削除
    myMap.erase(it);   // マップからポインタを削除
}

// QMap内のすべてのポインタを削除する場合
// QMap::clear() は値を削除しないので、手動で削除するか qDeleteAll を使う
qDeleteAll(myMap); // マップ内のすべての値を delete する(値がポインタの場合)
myMap.clear();     // マップをクリアする
// スマートポインタを使用する場合 (推奨)
#include <QSharedPointer>

QMap<QString, QSharedPointer<MyObject>> myMap;
myMap.insert("obj1", QSharedPointer<MyObject>(new MyObject()));
myMap.insert("obj2", QSharedPointer<MyObject>(new MyObject()));

myMap.erase(myMap.find("obj1")); // スマートポインタがスコープ外に出る際に自動的にメモリを解放
// QMap::clear() もスマートポインタが自動的にメモリを解放してくれる


例1: 特定のキーの要素を削除する

最も基本的な使い方で、QMap::find() でイテレータを取得し、それを erase() に渡します。

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

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

    QMap<QString, int> studentScores;
    studentScores.insert("Alice", 85);
    studentScores.insert("Bob", 72);
    studentScores.insert("Charlie", 90);
    studentScores.insert("David", 65);

    qDebug() << "--- 初期状態 ---";
    for (auto it = studentScores.begin(); it != studentScores.end(); ++it) {
        qDebug() << "学生:" << it.key() << ", スコア:" << it.value();
    }

    // 「Bob」のスコアを削除する
    QString studentToErase = "Bob";
    auto it = studentScores.find(studentToErase); // 'Bob' を探す

    if (it != studentScores.end()) { // 'Bob' が見つかった場合
        qDebug() << "\n--- '" << studentToErase << "' を削除 ---";
        studentScores.erase(it); // 'it' が指す要素(Bob)を削除
        qDebug() << "'" << studentToErase << "' を削除しました。";
    } else {
        qDebug() << "\n--- '" << studentToErase << "' は見つかりませんでした。 ---";
    }

    qDebug() << "\n--- 削除後の状態 ---";
    for (auto it_new = studentScores.begin(); it_new != studentScores.end(); ++it_new) {
        qDebug() << "学生:" << it_new.key() << ", スコア:" << it_new.value();
    }

    // 存在しないキーを削除しようとする例
    QString nonExistentStudent = "Eve";
    it = studentScores.find(nonExistentStudent);
    if (it != studentScores.end()) {
        qDebug() << "\n--- '" << nonExistentStudent << "' を削除 ---";
        studentScores.erase(it);
        qDebug() << "'" << nonExistentStudent << "' を削除しました。";
    } else {
        qDebug() << "\n--- '" << nonExistentStudent << "' は見つかりませんでした。 ---";
    }

    return 0;
}

解説

  • 存在する場合のみ studentScores.erase(it) を呼び出して削除します。
  • イテレータが studentScores.end() と等しくないことを確認することで、要素が実際に存在するかどうかをチェックします。
  • studentScores.find(studentToErase) で、削除したいキーに対応するイテレータを取得します。

例2: ループ内で条件に基づいて複数の要素を削除する

これが QMap::erase() の戻り値が最も重要になるシナリオです。ループ中に要素を削除すると、イテレータの無効化に注意が必要です。

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

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

    QMap<QString, int> productPrices;
    productPrices.insert("Laptop", 1200);
    productPrices.insert("Mouse", 25);
    productPrices.insert("Keyboard", 75);
    productPrices.insert("Monitor", 300);
    productPrices.insert("Webcam", 50);

    qDebug() << "--- 初期状態 ---";
    for (auto it = productPrices.begin(); it != productPrices.end(); ++it) {
        qDebug() << "製品:" << it.key() << ", 価格:" << it.value();
    }

    qDebug() << "\n--- 価格が100ドル以下の製品を削除 ---";

    // イテレータをループしながら削除する際の正しい方法
    auto it = productPrices.begin();
    while (it != productPrices.end()) { // ループの条件は常に it が end() でないこと
        if (it.value() <= 100) { // 価格が100ドル以下の場合
            qDebug() << "削除中:" << it.key() << "(価格:" << it.value() << ")";
            // erase() は削除された要素の次の要素を指すイテレータを返す
            it = productPrices.erase(it);
        } else {
            // 削除しない場合は、手動でイテレータを進める
            ++it;
        }
    }

    qDebug() << "\n--- 削除後の状態 ---";
    for (auto it_new = productPrices.begin(); it_new != productPrices.end(); ++it_new) {
        qDebug() << "製品:" << it_new.key() << ", 価格:" << it_new.value();
    }

    return 0;
}

解説

  • 削除しない場合 (else { ++it; }) は、通常のループのようにイテレータをインクリメントして次の要素に進みます。
  • 削除する場合 (it = productPrices.erase(it);) は、erase() の戻り値(次の要素を指す有効なイテレータ)をitに再代入します。これにより、無効なイテレータへのアクセスを防ぎます。
  • if (it.value() <= 100) で削除条件をチェックします。
  • while (it != productPrices.end()) でループします。

例3: スマートポインタを値として格納している場合の削除

メモリリークを防ぐためにスマートポインタを使用している場合の例です。この場合、erase() を呼び出すだけで、スマートポインタが自動的にメモリを解放してくれます。

#include <QCoreApplication>
#include <QMap>
#include <QDebug>
#include <QSharedPointer> // スマートポインタ

// ダミーのクラス
class MyData {
public:
    MyData(int id) : m_id(id) {
        qDebug() << "MyData" << m_id << "が構築されました。";
    }
    ~MyData() {
        qDebug() << "MyData" << m_id << "が破棄されました。";
    }
    int id() const { return m_id; }
private:
    int m_id;
};

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

    QMap<QString, QSharedPointer<MyData>> dataMap;

    dataMap.insert("data_A", QSharedPointer<MyData>(new MyData(101)));
    dataMap.insert("data_B", QSharedPointer<MyData>(new MyData(102)));
    dataMap.insert("data_C", QSharedPointer<MyData>(new MyData(103)));

    qDebug() << "\n--- 初期状態 ---";
    for (auto it = dataMap.begin(); it != dataMap.end(); ++it) {
        qDebug() << "キー:" << it.key() << ", データID:" << it.value()->id();
    }

    // "data_B" を削除
    QString keyToErase = "data_B";
    auto it = dataMap.find(keyToErase);

    if (it != dataMap.end()) {
        qDebug() << "\n--- '" << keyToErase << "' を削除 ---";
        // QSharedPointer は、QMapから削除される(参照カウントが0になる)と、
        // 参照しているMyDataオブジェクトを自動的に破棄します。
        dataMap.erase(it);
        qDebug() << "'" << keyToErase << "' を削除しました。";
    }

    qDebug() << "\n--- 削除後の状態 ---";
    for (auto it_new = dataMap.begin(); it_new != dataMap.end(); ++it_new) {
        qDebug() << "キー:" << it_new.key() << ", データID:" << it_new.value()->id();
    }

    // プログラム終了時、残りのMyDataオブジェクトも自動的に破棄されます。
    // (QMapが破棄される際に、中に格納されたQSharedPointerも破棄されるため)

    return 0;
}
  • dataMap.erase(it) を呼び出すと、マップから QSharedPointer オブジェクトが削除されます。そのQSharedPointerの参照カウントが0になれば、内部の MyData オブジェクトは自動的に破棄されます。これにより、メモリリークのリスクが大幅に軽減されます。
  • QSharedPointer<MyData> を使用することで、QMap から要素を削除する際に、生ポインタの場合のように手動で delete する必要がなくなります。


QMap::remove(const Key &key)

最も直接的で、特定のキーに対応する要素を削除したい場合に便利です。キーが存在しない場合は何も行いません。

特徴

  • 戻り値は削除された要素の数です。
  • QMultiMap の場合は、指定したキーを持つすべての要素を削除します。
  • QMap はキーがユニークなので、指定したキーに一致する要素は1つだけ削除されます。
  • 内部的には、まずキーを検索し、その後に要素を削除するという動作になります。
  • キーを指定して要素を削除します。

利点

  • イテレータの無効化を意識する必要がない(ループ中に使う場合は後述の注意が必要)。
  • コードが簡潔で読みやすい。

欠点

  • ループ中に使用する場合、イテレータが無効化されるため注意が必要です。
  • すでにイテレータを持っている場合でも、キーによる検索が内部的に行われるため、erase(iterator pos) よりも若干オーバーヘッドが発生する可能性があります。

使用例

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

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

    QMap<QString, int> studentScores;
    studentScores.insert("Alice", 85);
    studentScores.insert("Bob", 72);
    studentScores.insert("Charlie", 90);

    qDebug() << "--- 初期状態 ---";
    qDebug() << studentScores;

    // 「Bob」をキーで削除
    int removedCount = studentScores.remove("Bob");
    qDebug() << "\n'Bob' を削除しました。削除数:" << removedCount;

    qDebug() << "\n--- 削除後の状態 ---";
    qDebug() << studentScores;

    // 存在しないキーの削除
    removedCount = studentScores.remove("David");
    qDebug() << "\n'David' を削除しようとしました。削除数:" << removedCount; // 0が返る

    qDebug() << "\n--- その後の状態 ---";
    qDebug() << studentScores;

    // ループ中に remove() を使う場合の注意
    // これは QMap のイテレータの場合、it.key() でキーを取得し、そのキーを remove() に渡すことで機能します。
    // しかし、erase(it) のようにイテレータの自動更新はされないため、手動でイテレータを進める必要があります。
    // また、remove() はイテレータを無効化する可能性があります。
    // 確実には QMutableMapIterator を使うか、erase() の戻り値を使うべきです。

    QMap<QString, int> myMapForLoop;
    myMapForLoop.insert("A", 1);
    myMapForLoop.insert("B", 2);
    myMapForLoop.insert("C", 3);
    myMapForLoop.insert("D", 4);

    qDebug() << "\n--- ループと remove() の例 (推奨されないが動作はする) ---";
    auto it = myMapForLoop.begin();
    while (it != myMapForLoop.end()) {
        if (it.value() % 2 == 0) { // 偶数値のものを削除
            qDebug() << "Removing by key:" << it.key();
            // remove() は現在のイテレータを無効にする可能性があるため、
            // ポストインクリメント演算子 (it++) で次のイテレータを先に取得してから削除する手法が使われることがあります。
            // しかし、これは常に安全とは限らず、QMutableMapIterator や erase の戻り値を使う方が堅牢です。
            // この書き方は Qt のドキュメントでも推奨されていないので注意が必要です。
            myMapForLoop.remove((it++).key()); // 無効化される前に次のイテレータに進める
        } else {
            ++it;
        }
    }
    qDebug() << "Map after loop remove:" << myMapForLoop;
    // 上記の it++ を使った remove は、QMapの内部実装によっては問題を引き起こす可能性があります。
    // 一般的には erase(it) を使うか、QMutableMapIterator を使う方が安全です。

    return 0;
}

QMutableMapIterator を使用する

Qt 独自の Java スタイルのイテレータで、イテレータ自身が要素の削除機能を持っています。ループ中に要素を削除する際に非常に便利で、イテレータの無効化を自動的に処理してくれます。

特徴

  • remove() を呼び出した後も、イテレータは自動的に次の有効な位置に進むため、手動で ++it する必要がありません(削除しなかった場合は next() を呼び出す必要があります)。
  • remove() メソッドを持っており、現在のイテレータが指す要素を削除します。
  • QMap::erase() が STL スタイルのイテレータであるのに対し、QMutableMapIterator は Java スタイルのイテレータです。

利点

  • イテレータの無効化を意識する必要がない。
  • ループ内での要素削除が非常に安全かつ直感的。

欠点

  • 単一の要素を削除するだけなら QMap::remove()QMap::find() + QMap::erase() の方が簡潔です。
  • STL スタイルのイテレータに慣れている開発者には、API が少し異なって感じるかもしれません。

使用例

#include <QCoreApplication>
#include <QMap>
#include <QDebug>
#include <QMutableMapIterator> // QMutableMapIterator を使うにはこれが必要

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

    QMap<QString, int> itemQuantities;
    itemQuantities.insert("Laptop", 5);
    itemQuantities.insert("Mouse", 10);
    itemQuantities.insert("Keyboard", 3);
    itemQuantities.insert("Monitor", 7);
    itemQuantities.insert("Webcam", 0); // ゼロ個のアイテム

    qDebug() << "--- 初期状態 ---";
    for (auto it = itemQuantities.begin(); it != itemQuantities.end(); ++it) {
        qDebug() << "アイテム:" << it.key() << ", 数量:" << it.value();
    }

    qDebug() << "\n--- 数量が0のアイテムを削除 ---";

    // QMutableMapIterator を使用してループ中に削除
    QMutableMapIterator<QString, int> i(itemQuantities);
    while (i.hasNext()) {
        i.next(); // 次の要素に進む
        if (i.value() == 0) { // 数量が0の場合
            qDebug() << "削除中:" << i.key();
            i.remove(); // 現在の要素を削除。イテレータは自動的に次の有効な位置に進む。
        }
        // 削除しなかった場合は、i.next() によって既に進んでいるので、ここでは何もしない
    }

    qDebug() << "\n--- 削除後の状態 ---";
    for (auto it_new = itemQuantities.begin(); it_new != itemQuantities.end(); ++it_new) {
        qDebug() << "アイテム:" << it_new.key() << ", 数量:" << it_new.value();
    }

    return 0;
}

QMap::clear()

マップ内のすべての要素を一度に削除する場合に使用します。

特徴

  • 値がポインタ型の場合、clear() はポインタ自体を削除しますが、ポインタが指すメモリは解放しません(メモリリークの原因となります)。
  • 最も効率的な一括削除方法です。
  • マップ全体を空にします。

利点

  • すべての要素を削除する際に最も簡潔で高速。

欠点

  • 値がポインタの場合のメモリ管理に注意が必要です。
  • 個別の要素を削除する目的には使用できません。

使用例

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

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

    QMap<QString, int> myBigMap;
    for (int i = 0; i < 1000; ++i) {
        myBigMap.insert(QString("Key_%1").arg(i), i);
    }

    qDebug() << "初期マップのサイズ:" << myBigMap.size();

    // マップの全要素をクリア
    myBigMap.clear();

    qDebug() << "クリア後のマップのサイズ:" << myBigMap.size();

    // ポインタを格納している場合のメモリリーク対策
    QMap<QString, int*> pointerMap;
    pointerMap.insert("data1", new int(10));
    pointerMap.insert("data2", new int(20));

    // メモリリークを防ぐために、qDeleteAll() を使用してポインタが指すメモリを解放
    qDeleteAll(pointerMap); // マップ内のすべての値を delete する(値がポインタの場合のみ有効)
    pointerMap.clear();     // マップをクリアする

    // もしくはスマートポインタを使用する
    // QMap<QString, QSharedPointer<int>> smartPointerMap;
    // smartPointerMap.insert("data3", QSharedPointer<int>(new int(30)));
    // smartPointerMap.clear(); // QSharedPointerが自動的にメモリを解放

    return 0;
}

Qt 6.1以降で導入された std::erase_if スタイルのグローバル関数

Qt 6.1 以降では、C++ 標準ライブラリの std::erase_if に似た、述語(Predicate)に基づいて要素を削除するグローバル関数が提供されています。これは QMap のメンバ関数ではありませんが、特定の条件に合致する複数の要素を効率的に削除するのに非常に便利です。

// これは QMap のメンバ関数ではありません。グローバル関数として提供されます。
// QMap 内の要素で、与えられた述語が true を返すものをすべて削除します。
// 戻り値は削除された要素の数です。
template <typename Key, typename T, typename Predicate>
qsizetype erase_if(QMap<Key, T> &map, Predicate pred);

特徴

  • ラムダ式と組み合わせることで、非常に柔軟な条件指定が可能です。
  • イテレータの無効化を開発者が気にする必要がありません。
  • 特定の条件に合致するすべての要素を一度に削除できます。

利点

  • 安全で効率的です。
  • ループを書く手間が省け、コードがより簡潔になります。

欠点

  • Qt 6.1 以降でのみ利用可能です。
#include <QCoreApplication>
#include <QMap>
#include <QDebug>
#include <QtAlgorithms> // erase_if を使うために必要

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

    QMap<QString, int> sensorReadings;
    sensorReadings.insert("Sensor1", 10);
    sensorReadings.insert("Sensor2", 5);
    sensorReadings.insert("Sensor3", 12);
    sensorReadings.insert("Sensor4", 3);
    sensorReadings.insert("Sensor5", 8);

    qDebug() << "--- 初期状態 ---";
    qDebug() << sensorReadings;

    qDebug() << "\n--- 値が5以下のセンサーデータを削除 ---";

    // erase_if を使用して削除
    qsizetype removedCount = Qt::erase_if(sensorReadings, [](const auto &pair) {
        return pair.second <= 5; // 値が5以下の場合にtrueを返すラムダ
    });

    qDebug() << "削除された要素数:" << removedCount;

    qDebug() << "\n--- 削除後の状態 ---";
    qDebug() << sensorReadings;

    return 0;
}
削除方法ユースケースイテレータの安全性効率備考
QMap::erase(iterator pos)特定の場所の要素を削除、ループ中に条件削除要注意 (戻り値利用)O(logn)STLスタイル。最も柔軟なイテレータ制御が可能。
QMap::remove(const Key&)特定のキーの要素を削除安全O(logn)簡潔だが、ループ中のイテレータ扱いに注意。
QMutableMapIterator::remove()ループ中に条件に基づいて要素を削除安全 (自動調整)O(logn)Javaスタイル。ループ中の削除に最適。
QMap::clear()全ての要素を削除安全O(n)全クリアに特化。ポインタのメモリ解放は別途必要。
Qt::erase_if()条件に合致する複数の要素を効率的に削除安全O(n)Qt 6.1以降。ラムダと組み合わせて強力。