備忘録

プログラムやゲーム関連に関すること

【C++】標準コンテナでの値の削除

標準コンテナでの値の削除にはコンテナの種類に適した削除方法を選択する

test.cpp

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <list>
#include <map>
#include <string>

using namespace std;

// 型の宣言
using my_map = map < string, int > ;
using my_pair = pair < string, int > ;

// 値の表示関数
template<typename T>
void show(const T &seq)
{
	copy(seq.begin(), seq.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
}

// map 用に表示関数の特殊化
template<>
void show(const my_map &m)
{
	for_each(m.cbegin(), m.cend(),
		[](const my_pair &it){ cout << "key: " << it.first << " value: " << it.second << endl; });
}

int main(int argc, char *argv[])
{
	// 値の評価用
	auto greater = [](int x){ return x > 3; };

	// シーケンスコンテナで remove を使う方法では線形時間を必要とする
	// 連続メモリコンテナでの要素の削除 (vector, deque, string)
	vector<int> v1{ 0, 1, 2, 3, 4, 5 };

	// erase と remove の慣用的用法が効率が良い
	v1.erase(remove(v1.begin(), v1.end(), 3), v1.end());
	show(v1); // 0 1 2 4 5

	v1.erase(remove_if(v1.begin(), v1.end(), greater), v1.end());
	show(v1); // 0 1 2

	// list での要素の削除
	list<int> l{ 0, 1, 2, 3, 4, 5 };

	// 上記の慣用的用法も利用できるが、リストは remove が効率が良い
	l.remove(3);
	show(l); // 0 1 2 4 5

	l.remove_if(greater);
	show(l); // 0 1 2

	// 標準連想コンテナでの要素の削除 (map, multimap, set, multiset)
	// 標準連想コンテナの場合、remove を使用することはできない
	// アルゴリズムの remove を使用するとデータ破損の可能性やコンパイルがされない
	my_map m{ { "a", 0 }, { "b", 1 }, { "c", 2 }, { "d", 3 } };

	// 標準連想コンテナでは erase が最も良い方法
	m.erase("b"); // key で削除する
	show(m); // key: a value: 0\n key: c value: 2\n key: d value: 3\n

	// 標準連想コンテナの述語評価での値の削除
	auto check = [](const my_pair &it){ return it.second > 0; };

	// 一時データに評価した値をコピーしてスワップするためコピーの対象が多い場合は非効率となる
	//my_map tmp;
	//remove_copy_if(m.begin(), m.end(), inserter(tmp, tmp.end()), check);
	//m.swap(tmp);
	//show(m); // key: a value: 0\n
	
	// 連想コンテナで一時データを作らず削除する場合はループ処理で解決する
	// while ループでも良いが for ループではイテレータの走査が
	// for ループのローカル範囲なので保守性を考慮することができる
	for (auto it = m.begin(); it != m.end();)
	{
		if (check(*it)) m.erase(it++);
		else ++it;
	}
	show(m);

	// また、「連続メモリコンテナ」でループ処理を利用した値の削除は
	// 無効化された次の要素を指すイテレータを受け取る必要がある
	// これは erase を呼び出すと要素を指すすべてのイテレータが無効化されるだけでなく
	// 削除される要素以降のすべてのイテレータが無効化されるためである
	vector<int> v2{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (auto it = v2.begin(); it != v2.end();)
	{
		if (greater(*it)) it = v2.erase(it);
		else ++it;
	}
	show(v2); // 0 1 2 3

	getchar();

	return 0;
}

参考:Effective STL