関数ポインタによる関数の扱い方は以下のように書いていました。

#include <iostream>

using namespace std;

void func1()
{
	cout << "Call Func" << endl;
}

int func2(int a, int b)
{
	return a + b;
}

int main()
{
	void (*p1)() = func1;
	p1();

	int (*p2)(int, int) = func2;
	cout << (*p2)(1, 2) << endl;

	return 0;
}

C++11ではfunctionalヘッダ内に新しくfunctionという機能が追加され、上記と似たことが出来るようになりました。

#include <iostream>
#include <functional>

using namespace std;

void func1()
{
	cout << "Call Func" << endl;
}

int func2(int a, int b)
{
	return a + b;
}

int main()
{
	//テンプレートに登録する関数と同じ戻り値、引数を指定する
	function<void()> f1 = func1;
	f1();

	function<int(int, int)> f2 = func2;
	cout << f2(1, 2) << endl;

	return 0;
}

また、このfunctionは様々な形式に対して使用することが出来ます。

#include <iostream>
#include <functional>

using namespace std;

void func()
{
	cout << "Call Func" << endl;
}

class Obj
{
public:
	void func()
	{
		cout << "Call Obj Func" << endl;
	};
};

int main()
{
	//テンプレートに登録する関数と同じ戻り値、引数を指定する
	function<void()> f;
	
	//通常の関数
	f = &func;
	f();

	//ラムダ関数
	f = []()->void{
		cout << "Call Lambda Func" << endl;
	};
	f();

	//メンバ関数
	Obj obj;
	f = bind(mem_fn(&Obj::func), &obj);
	f();

	function<void()> f2 = &func;
	f2();

	//他のfunctionと中身を入れ替えることも可能
	f2.swap(f);
	f2();

	return 0;
}

この時出てきたbind関数はある引数を固定した状態で使用するといった時に使用します。

#include <iostream>
#include <functional>
#include <cmath>

using namespace std;

struct Pos
{
	double x, y;
};

double func(const Pos& s, const Pos& e)
{
	//xの2乗と、yの2乗の和の平方根
	return hypot(s.x - e.x, s.y - e.y);
}

int main()
{
	Pos s{5.0, 5.0};

	//function側には固定しない引数のみを受け取る書き方にする
	//refは参照渡しの引数を固定する場合に使う(値渡しなら単純にsにする)
	//placeholderは名前空間で、これをつかうことにより束縛対象にしない引数を指定出来る
	function<double(Pos&)> f = bind(func, ref(s), placeholders::_1);

	cout << f(Pos{-5.0, -5.0}) << endl;
	cout << f(Pos{5.0, 5.0}) << endl;
	cout << f(Pos{10.0, 10.0}) << endl;

	return 0;
}

coutによる出力部分を見ると分かりますが、引数一つの関数のような振る舞いでfuncを呼び出すことが出来ます。上記のプログラムの例の様に、ある座標を固定してそれ以外の座標との距離を計算する場合のような、引数の要素を固定する問題を扱う時に書き間違いを減らせたり、書くことが決まっている引数を書かずに済ますことが出来ます。

最後に簡単な例として、functionを使った簡単な状態遷移の方法を紹介します。

#include <iostream>
#include <functional>
#include <array>
#include <random>

using namespace std;

enum class State : unsigned
{
	Title,
	Game,
	Fail,
	Clear,
	StateMax,
};

int main()
{
	//Stateを戻り値とした関数を受け取るfuntionを状態数分持った配列
	array<function<State()>, static_cast<unsigned>(State::StateMax)> state;
	random_device rd;

	//タイトル
	state[static_cast<unsigned>(State::Title)] = []()->State
	{
		cout << "State Title" << endl;
		return State::Game;
	};

	//ゲームメイン
	state[static_cast<unsigned>(State::Game)] = [&rd]()->State
	{
		cout << "State Game" << endl;
		return rd()%2 ? State::Clear : State::Fail;
	};

	//失敗
	state[static_cast<unsigned>(State::Fail)] = []()->State
	{
		cout << "State Fail" << endl;
		return State::Title;
	};

	//クリアー
	state[static_cast<unsigned>(State::Clear)] = []()->State
	{
		cout << "State Clear" << endl;
		return State::Title;
	};

	//最初の状態を指定
	State nowState = State::Title;

	//適当にループ
	int i = 100;
	while(i--)
	{
		//現在の状態を実行して、戻り値として次の状態を受け取る
		nowState = state[static_cast<unsigned>(nowState)]();
	}

	return 0;
}

main関数しか記述していないですが、ラムダ関数を利用することによって状態遷移が簡単に作成でき、小規模な実装等の場合はこれを使うと非常に便利に書くことができます(ちなみに、KUS3回で作ったブロック崩しは、この書き方で状態遷移を作っていて、main関数のみで構成されていたりします)。
※大規模な処理を伴う状態遷移を行う場合は、ゲームシステム講座2回(作:アブナイお兄ちゃん)の方法を使用したほうがいいです。

前⇒続・C++11講座2回
次⇒続・C++11講座4回


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-12-15 (月) 16:14:17