std::experimental::erase/erase_if
新しい処理系を手に入れてリリースノートに目を通し、その次に僕がやるのはインクルードとライブラリディレクトリの徘徊です。目新しい機能の多くはここらへんに現れますからね。Visual C++ 14のinclude配下に、いかにも怪しげ(?)なディレクトリ:experimentalを見つけました。

<vector>, <list>, <set>などなど、おなじみの標準コンテナたちが集結しています。なにかよほど大きなexperimental(お試し)が行われているのかとエディタで開いてみました。

<list>や<set>などなども同様……どうやら標準コンテナを引数に取るアルゴリズム:erase/erase_ifが定義されているようです。そういえば標準アルゴリズム:<algorithm>に用意されているのはremove/remove_ifであって、"erase"と名の付く関数テンプレートは見当たりません。
英和辞典を引いてみると removeは「除外する」、eraseは「消去する」と書かれていました。意味合いが異なるんですね……なるほど、思い当たるフシがあります。
vector<int>から特定の要素をremoveしてみましょう:
vector<int> container = { 0, 1, 2, 3, 0, 1, 2, 3, 0 };
// containerから0をremoveする
auto last = remove(begin(container), end(container), 0);
for_each(begin(container), last, [](auto item) { cout << item << ' ';});
cout << endl;
cout << "... but actually in vector:" << endl;
for (auto item : container) { cout << item << ' '; }
cout << endl;
last = remove(p, q, val) は範囲[p,q) からvalと等しい要素を除外し、イテレータ(last)を返します。これにより、範囲[begin(container), last) からはval(この例では0)が消えていますが、containerの要素数に変化はなく、範囲[last, end(container)) にはremove前の要素がそのまま残っています。除外(remove)するけど消去(erase)してはくれません。なのでcontainer内から消去したいなら、lastからcontainer(end)までを明示的に消去しなければなりません。
// 上記に続いて……
cout << "so, erase from last to end." << endl;
container.erase(last, end(container));
for (auto item : container) { cout << item << ' '; }
cout << endl;

関数removeはコンテナが持つ消去系メンバ関数:erase()やpop_back()などを呼ぶすべがないのだから当然っちゃ当然(でもビギナはよくハマる)なのですが。
set/map系すなわち連想コンテナ(associative-container)からの要素の消去は、さらにややこしくなります:
multiset<int> container = { 0, 1, 2, 3, 0, 1, 2, 3, 0 };
// multiset container から 0 を消去する
auto iter = begin(container);
while( iter != end(container) ) {
if ( *iter == 0 ) {
iter = container.erase(iter);
} else {
++iter;
}
}
for (auto item : container) { cout << item << ' '; }
cout << endl;
「コンテナから特定の値を持つ/条件を満たす要素を消去する」なんて基本的な操作じゃないですか。なのに、こんな面倒なコードを書かにゃならんのです。これを楽にしてくれるのが<experimental>にあるコンテナ・ヘッダに追加されたstd::experimental::erase/erase_ifです。
erase/erase_ifの第一引数にはイテレータではなく、コンテナそのもの(参照)を与えます。コンテナそのものを引き渡すので、コンテナのメンバ関数eraseを呼び出して消去できるってカラクリですね。
vector<int> container = { 0, 1, 2, 3, 0, 1, 2, 3, 0 };
std::experimental::erase(container, 0); // containerから0を消去する
// ……消えたかな?
for (auto item : container) { cout << item << ' '; }
cout << endl;
この拡張eraseがサポートされるのは標準コンテナ:
- deque
- forward_list
- list
- map / multimap
- set / multiset
- basic_string (string/wstring)
- unordered_map / unordered_multimap
- unordered_set / unordered_multiset
- vector
このうち、set/map系については(eraseはメンバ関数に定義済みなので)erase_ifのみ追加されています。
using namespace std;
map<string, string> container = {
{ "りんご", "apple" },
{ "みかん", "orange" },
{ "ぶどう", "grape" },
{ "いちご", "strawberry" },
{ "バナナ", "banana" },
};
// 和英辞典(container)から、英単語に'p'を含む要素を消去する
experimental::erase_if(container,
[](auto item) { return item.second.find('p') != string::npos; });
// ……消えたかな?
for (auto item : container) { cout << item.first << ':' << item.second << endl; }
cout << endl;

