HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

mapのoperator[]の罠

mapを使ったあるプログラムを書いていて、やたらとメモリを食うので、何だろうといろいろ試行錯誤しながら調べてみたら、どうもmapの使い方に問題があるのではないかということが分かってきた。

mapのリファレンスを探して読んでみると、以下のようなことが書いてある。

https://cpprefjp.github.io/reference/map/map/op_at.html

戻り値

キーxに対応する値を返す。対応する要素が存在しない場合は、要素をデフォルト構築して参照を返す。

「デフォルト構築」ってなんぞや?・・・

そこで、(いろいろすっ飛ばして)検証プログラムを書いてみた。

#include <iostream>
#include <map>

using namespace std;

int main(int argc, char** argv) {
    map<int, int*> m;

    int a = 1;
    int b = 2;

    // (1)
    {
        map<int, int*>::iterator it = m.find(1);
        cout << "(1) " << (it == m.end()) << endl;
    }

    // (2)
    {
        int* p = m[1];
        cout << "(2) " << (p == NULL) << endl;
    }

    // (3)
    {
        map<int, int*>::iterator it = m.find(1);
        cout << "(3) " << (it == m.end()) << ":" << (it->second == NULL) << endl;
    }

    // (4)
    {
        m[1] = &a;
        map<int, int*>::iterator it = m.find(1);
        cout << "(4) " << (it == m.end()) << ":" << (it->second == NULL) << ":" << (it->second == &a) << endl;
    }

    return EXIT_SUCCESS;
}

出力は以下のようになる。

(1) 1
(2) 1
(3) 0:1
(4) 0:0:1

(1)ではitm.end()に等しいのに、(2)でm[1]をやった後に(3)でもう一度findを使うと、今度はm.end()と等しくならなくなっている、つまり、mapの中に何かしらのエントリが存在する状態になっているようだ。

(4)はついでだが、m[1]に何かを代入すると、当然ながらその値がmapに入ることになる。

これは罠だなぁ‥覚えておこう‥