Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Visual C++ 14で見つけた<experimental>(試験的)な機能

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2015/11/30 14:00

 Windows 10に併せてリリースされたVisual Studio 2015、哀しいことに(インストールは済ませたものの)いまだにじっくりと触ることができていません。いつもならせっせとプロジェクトを引っ越し、ピカピカの環境で遊んでいるはずなのに……。いろんなオトナの事情でもうしばらくはVisual Studio 2013に留まらざるを得ないのですが、このままほったらかして、ずるずると遅れをとってるわけにもいかないので、ちょっとのヒマを見つけては新しいVisual C++ 14の味見を続けています。

目次

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してみましょう:

list01
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)までを明示的に消去しなければなりません。

list02
// 上記に続いて……
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)からの要素の消去は、さらにややこしくなります:

list03
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を呼び出して消去できるってカラクリですね。

list04
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のみ追加されています。

list05
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;

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • επιστημη(エピステーメー)

    C++に首まで浸かったプログラマ。 Microsoft MVP, Visual C++ (2004.01~) だったり わんくま同盟でたまにセッションスピーカやったり 中国茶淹れてにわか茶人を気取ってたり、 あと Facebook とか。 著書: - STL標準講座 (監修) -...

All contents copyright © 2005-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5