HEXA BLOG

プログラム

HEXA BLOGプログラム2015.12.2

C++11を使おう

どうも2回目の登場です。プログラマのしぶっちです。

 

突然寒くなってきましたが、みなさん体調など崩されていないでしょうか?私は先週末幕張メッセで喉をやられて来ましたが、年末の有明は問題なさそうです。

 

 

今まで主にC++でプログラムをさせて頂いたのですが、環境や過去のしがらみでC++11に移行していない環境も有りました。 C++11に対応していないコンパイラの時は仕方無いんですが、使えるのに使わないのはもったいない。

・・・と言っても私自身まだまだ使いこなせているわけではないので、特に移行の必要を感じていない方向けに C++11環境で特に便利になったなぁという所を軽く紹介して興味持ってもらえれば。

 

便利になった所1 autoと範囲ベースfor

まずはauto型です。auto型はコンパイラ側で変数型が確定出来る時に変数型を詳しく書かなくて良くなります。 例えば、単純に0~9の数列内を順に表示するだけのプログラムですが、

void index_disp() {
	std::vector indexVec;

	for(unsigned int i = 0; i < 10; ++i) {
		indexVec.push_back(i);
	}

	for(std::vector::iterator it = indexVec.begin(); it != indexVec.end(); ++it) {
		std::cout << "index: " << *it << std::endl;
	}
}

>> for(std::vector::iterator it = indexVec.begin(); it != indexVec.end(); ++it) {

ここが長い!今はunsigned intだからまだマシですが、テンプレートとか増えだして<>が何個も増えてきたりすると for分だけで数行使います。

auto使いましょう

>> for(auto it = indexVec.begin(); it != indexVec.end(); ++it) {

すっきり!

昔はわざわざtypedefしたりしていたものですがautoでその必要も無くなり、更に見やすくなります。 ちなみにbegin,endは以下の様にも書けます。

>> for(auto it = begin(indexVec); it != end(indexVec); ++it) {

ただ、これ見やすいと思って置き換えしてたらreverse iteratorのrbegin,rendはC++14からの様で揃えられなくて戻しました・・・。

またダミーデータの生成はC++11だとiotaが使えます。

initializer list 使って初期化も有りです。VisualStudio2012迄だと使えないようですが・・・。
void index_disp2() {
	std::vector indexVec(10);
	iota(begin(indexVec), end(indexVec), 0);
	
	for(auto it = indexVec.begin(); it != indexVec.end(); ++it) {
		std::cout &lt;&lt; "index: " &lt;&lt; *it &lt;&lt; std::endl;
	}
}

そして同時にC++11で定義された範囲ベースのforと組み合わせるとこうなります。

void index_disp3() {
	std::vector indexVec(10);
	iota(begin(indexVec), end(indexVec), 0);

	for(auto index :indexVec) {
		std::cout &lt;&lt; "index: " &lt;&lt; index &lt;&lt; std::endl;
	}
}

この部分ですね。

>> for(auto index :indexVec) {

これはindexVec内の全ての要素をauto型で取得して処理しています。他言語だとforeachですね。 この場合インデックス値でアクセスするのではなく内部の要素を直接取得できます。 ですので、数値の表示も*itを使ったものから生の変数indexを出力しています。 forループ内で要素を書き換えたい場合はauto&で受ければ書き換えれます。 でもインデックス欲しい時も有るんで使い分けで。

 

便利になった所2 ラムダ式

次はラムダ式です。C#でラムダ式が実装された時は便利な物が出来たなぁと思った物ですが、C++11でも実装されています。 まずは先程のプログラムから偶数の数をカウントするプログラムです。

void count_even() {
	std::vector indexVec(10);
	iota(begin(indexVec), end(indexVec), 0);

	int count = 0;
	for(auto index :indexVec) {
		if(0 == index % 2) {
			++count;
		}
	}
	std::cout &lt;&lt; "even num: " &lt;&lt; count &lt;&lt; std::endl;
}

さてこれを昔のやり方でstd::count_ifを使うとこんな感じです。

void count_even() {
	std::vector indexVec(10);
	iota(begin(indexVec), end(indexVec), 0);
	
	struct isEven {
		bool operator()(int x) const { return (0 == (x % 2)); }
	};

	int count = std::count_if(indexVec.begin(), indexVec.end(), isEven());
	
	std::cout &lt;&lt; "even num: " &lt;&lt; count &lt;&lt; std::endl;
}

検索条件毎に比較用の関数オブジェクト作るとか厳しいですね。 このサンプルの場合偶数探してるだけなんで簡単だし共通で使うかもしれませんが、独自クラスをvector等に突っ込んで、 特殊条件で探す場合とかは面倒ですし、条件コードが別のところに書かれるのも微妙。

これがラムダ式だとこう。

void count_even2() {
	std::vector indexVec(10);
	iota(begin(indexVec), end(indexVec), 0);

	int count = std::count_if(indexVec.begin(), indexVec.end(), [](unsigned int index) { return (0 == index % 2); });

	std::cout &lt;&lt; "even num: " &lt;&lt; count &lt;&lt; std::endl;
}

ラムダ式の部分が最初は分からないと思いますが、構文を理解すると条件文がその場に有るので分かりやすいです。

ただ同じような条件で何度も検索するなら関数オブジェクト作った方が良いですね。

 

便利になった所3 unique_ptr

スマートポインタ自体は以前から存在していましたが格段に使いやすくミスが減るunique_ptrが実装されてます。 これはうまいサンプル思いつかなかったです。

class hogehoge {
	public:
		hogehoge() { std::cout &lt;&lt; "new hoge" &lt;&lt; std::endl; };
		~hogehoge() {std::cout &lt;&lt; "delete hoge" &lt;&lt; std::endl; };

};
void unique_test() {
	std::unique_ptr  hoge(new (std::nothrow) hogehoge);
}
// 出力
// new hoge
// delete hoge

本当はmake_unique使ってnewも見えないようにしたい所ですが、C++14まで我慢です。(VisualStudio2013以降では使える模様です。)

他にもstd::threadが簡単便利だったり、emplace_backを使用してコピーコスト減らしたりも出来るんですが、 その辺は昔のビッシーの記事とか参考資料等あたって見てください。

 

参考資料

Effective Modern C++ Scott Meyers 著 千住 治郎 訳 オライリー・ジャパン

ゲーム開発者のための C++11/C++14

ムーブセマンティクスってどうなるの? 

 

という事で今日はJリーグチャンピオンシップ決勝第一戦ですね。これから万博行ってきます。

RECRUIT

大阪・東京共にスタッフを募集しています。
特にキャリア採用のプログラマー・アーティストに興味がある方は下のボタンをクリックしてください

RECRUIT SITE 

NEWS