0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++にもdefaultdictが欲しいよって話

Last updated at Posted at 2025-02-14

 C++の map はとても便利です。

 高速なのもそうですが、存在しないキーを参照した時には 0 を返してくれるため、実装時のエラーハンドリングが不要になります。しかし、時としてこの仕様が仇になることがあります。

 具体的には、0 を単位元として扱えないとき、よりわかりやすく言うと、最小値更新や最大値更新のときです。

#include <iostream>
#include <map>
#include <algorithm>

using namespace std;

int main() {
    map<int, int> mp;
    mp[1] = min(2, mp[1]);
    cout << mp[1] << endl; // 0!?
}

 最小値は 2 で更新されるはずですが 0 になっています。

#include <iostream>
#include <map>
#include <algorithm>

using namespace std;

int main() {
    map<int, int> mp;
    mp[1] = max(-2, mp[1]);
    cout << mp[1] << endl; // 0!?
}

 最大値は -2 で更新されるはずですが 0 になっています。

 例えば、最小値だと初期値は十分大きな値、最大値だと初期値は十分小さな値、一般的には、ある演算に対して初期値を単位元にしないとバグります。

defaultfict

 Python においては、map に相当するデータ構造として defaultfdict があります。(ビルトインの dict だと、存在しないキーを参照した時にエラーが出るため、ハンドリングが必要になります)

 defaultdict はその名の通り初期値をカスタマイズできます。

from collections import defaultdict

# 最小値を求める場合(十分大きな初期値を設定)
min_map = defaultdict(lambda: 10**18)
min_map[1] = min(2, min_map[1])
print(min_map[1])  # 出力: 2

# 最大値を求める場合(十分小さな初期値を設定)
max_map = defaultdict(lambda: -10**18)
max_map[1] = max(-2, max_map[1])
print(max_map[1])  # 出力: -2

C++でもdefaultdictを

 これと同じものを C++ でも定義しましょう。
 以下のように、map をオーバーライドして default_map を作ります。

template <typename S, typename T>
struct default_map : map<S, T> {
    T default_value;

    // コンストラクタで初期値を受け取る
    default_map(T default_value) : default_value(default_value) {}

    // []演算子をオーバーライドして、キーがない場合にデフォルト値を設定
    T& operator[](const S& key) {
        return map<S, T>::emplace(key, default_value).first->second;
    }
};

使用例

#include <iostream>
#include <map>
#include <algorithm>

using namespace std;

template <typename S, typename T>
struct default_map : map<S, T> {
    T default_value;

    // コンストラクタで初期値を受け取る
    default_map(T default_value) : default_value(default_value) {}

    // []演算子をオーバーライドして、キーがない場合にデフォルト値を設定
    T& operator[](const S& key) {
        return map<S, T>::emplace(key, default_value).first->second;
    }
};

int main() {
    default_map<int, int> min_map(2e9);  // 十分大きな初期値(最小値用)
    default_map<int, int> max_map(-2e9); // 十分小さな初期値(最大値用)

    min_map[1] = min(2, min_map[1]);
    cout << "min_map[1]: " << min_map[1] << endl;  // 出力: 2

    max_map[1] = max(-2, max_map[1]);
    cout << "max_map[1]: " << max_map[1] << endl;  // 出力: -2

    return 0;
}

追記

 コメントによると、あまり良くない実装らしいので、ご注意ください。一応、競プロのようなインスタントな用途を想定しています。

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?