読者です 読者をやめる 読者になる 読者になる

C++と色々

主にC++やプログラムに関する記事を投稿します。

静的ポリモーフィズムとタグ・ディスパッチ

折角なのでプログラミングの記事を書いてみようと思う。

ポリモーフィズムについて。

ざっくり言うと、同名の関数や型が所属しているクラスによって振る舞いを変化させること。
継承によって実現できる。

#include <iostream>

class Parent
{
public:
	virtual void Show()
	{
		std::cout << "Parent::Show" << std::endl;
	}
};

class Child : public Parent
{
public:
	void Show()
	{
		std::cout << "Child::Show" << std::endl;
	}
};

int main()
{
	Parent* parentArray[5];
	
	for(int i = 0; i < 4; i++)
	{
		parentArray[i] = i % 2 ? new Parent() : new Child();
	}
	

	for(int i = 0; i < 4; i++)
	{
		parentArray[i]->Show();
	}

	for(int i = 0; i < 4; i++)
	{
		delete parentArray[i];
	}

	return 0;
}

実行結果

Child::Show
Parent::Show
Child::Show
Parent::Show
続行するには何かキーを押してください . . .

これは、実行時に仮想関数テーブルが作成されテーブルを参照してどの関数が呼ばれるのか決定するので、仮に動的ポリモーフィズムと呼ぶ。

これに対しtemplateを用いたポリモーフィズムはコンパイル時に決定されるので静的ポリモーフィズムと呼ぶ。
その実現方法は、タグ構造体を用いてテンプレート関数をオーバーロードすれば良い。この方法をタグ・ディスパッチと呼ぶ。

#include <iostream>

struct tag_int {};
struct tag_double {};
struct tag_char {};
struct tag_string {};


template<class T>
void _print(const T& value, tag_int)
{
	std::printf("%d\n", value);
}

template<class T>
void _print(const T& value, tag_double)
{
	std::printf("%f\n", value);
}

template<class T>
void _print(const T& value, tag_char)
{
	std::printf("%c\n", value);
}

template<class T>
void _print(const T& value, tag_string)
{
	std::printf("%s\n", value);
}

template<class T>
void print(const T& value)
{
	_print(value, T::tag());
}

class Int
{
	int value;
public:
	operator int(){return value;}
	Int(int i) : value(i){}
	typedef tag_int tag;
};

class Double
{
	double value;
public:
	operator double(){return value;}
	Double(double d) : value(d){}
	typedef tag_double tag;
};

class Char
{
	char value;
public:
	operator char(){return value;}
	Char(char c) : value(c){}
	typedef tag_char tag;
};

class String
{
	const char* value;
public:
	operator const char*(){return value;}
	String(const char* str) : value(str){}
	typedef tag_string tag;
};

int main()
{
	Int i(10);
	Double d(2.72);
	Char c('A');
	String str("Hello");
	
	print(i);
	print(d);
	print(c);
	print(str);

	return 0;
}

実行結果

10
2.720000
A
Hello
続行するには何かキーを押してください . . .

std::coutではオーバーロードする意味が無いので、printfのフォーマット指定子を利用した。
実際には、std::advance関数で、渡されたSTLコンテナのイテレータの種類によってイテレータの進め方(++か++--か+=か)
を変えたりするのに用いられている。

参考文献
επιστημη、高橋 晶:"C++テンプレートテクニック"、SoftBankCreative(2009)
"(void*)ないと":http://d.hatena.ne.jp/pknight/20100319/1268987210(2012/6/14アクセス)
"Code++":http://ameblo.jp/woooh-p/entry-10036646454.html(2012/6/15アクセス)