type erasureとは、C++みたいな静的型付け言語でduck typingもどきを実現する為のテンプレート魔術の事。ぐぐると色々出てくる。
単に色んな型を渡せるようにするだけならば、メンバ関数テンプレートでOK。
#include <iostream> class Foo { public: template<class T> void func(T &x) { std::cout << x.getName() << std::endl; } };
メンバ関数テンプレートだけで事足りなくなるのは、func()のT型の引数をメンバ変数に保存しようとした時。メンバ変数の型が書けない。
これを実現するのがtype erasure。T型の変数を保持しておくテンプレートクラスを用意する事によって、T型の変数を保持する事が出来る。
#include <iostream> #include <string> class BaseHolder { public: virtual ~BaseHolder(){} virtual std::string GetName(void) const = 0; }; // BaseHolderを継承するテンプレートクラス // T型の引数はこのクラスに保存できる template<class T> class FooHolder : public BaseHolder { T obj; public: FooHolder(const T &x) : obj(x) {} virtual ~FooHolder() {} virtual std::string GetName(void) const { return obj.GetName(); } }; // duck typingもどきをするメンバ関数テンプレートを持つクラス class Foo { BaseHolder *obj; public: Foo() : obj(NULL) { } virtual ~Foo() { if(obj) delete obj; } template<class T> void Set(const T &x) { obj = new FooHolder<T>(x); } void Call(void) { std::cout << obj->GetName() << std::endl; } };
これで以下のコードが書けるようになります。
// メンバ関数 GetName() を持つクラスその1 class A { public: std::string GetName(void) const { return "Class A"; } }; // メンバ関数 GetName() を持つクラスその2 class B { public: std::string GetName(void) const { return "Class B"; } }; int main(void) { Foo obj; A a; B b; obj.Set(a); obj.Call(); obj.Set(b); obj.Call(); return 0; }