C++ で ダック・タイピング
C++ ではメソッドの名前が同じでも返り値が違うと継承時にオーバーライドできない(当たり前だけれど).例えば以下の様に,親子関係にある二つのクラスでそれぞれの配列を作るとする.
class BaseObj { ... }; class DerivObj : public BaseObj { ... }; class BaseArray { virtual BaseObj* Get(int index) { /* Get の実装 */ } }; class DerivArray : BaseArray { virtual DerivObj* Get(int index); /* 上の実装を再利用したい */ }
Array クラス内でどのように配列を持つかは任意としても,きっと BaseArray と DerivArray は対象のオブジェクトが違うだけで,内容はほとんど一緒になるだろう.当然の思いとして,上のように DerivArray で BaseArray を継承しようと考える.
だけれど,これはエラーになる.なぜなら,BaseArray::Get と DerivArray::Get は返り値が違うのでオーバーライドできず,でも名前が一緒なのでオーバーロードの関係になってて,戻り値しか違わないのでオーバーロードできずコンパイルエラーになる.実装は再利用したいけれど,継承では解決できない.
そこで華麗に登場するのがダック・タイピングという手法.俺もさっき知った.そうそう,俺がやりたかったのはこういうことなんだよ!
ダックタイピングとは ... オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせるということである
http://ja.wikipedia.org/wiki/%E3%83%80%E3%83%83%E3%82%AF%E3%83%BB%E3%82%BF%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0
つまり,オーバーライドの関係になくても,同じ名前なら同じようにメソッドを呼び出せる,ということ.C++ の場合は,静的ならば(コンパイル時に型が決まるならば)テンプレートを使ってできるみたい.
これを参考に,上のコードを書き換えると次の様になる.
class BaseObj { ... }; class DerivObj : public BaseObj { ... }; template <class Obj> class Array { Obj* Get() { /* Get の実装 */ } }; // テンプレートのインスタンス化 template class Array<BaseObj>; template class Array<DerivObj>; class BaseArray : public Array<BaseObj> { } class DerivArray: public Array<DerivObj> { }
コードのコピペをコンパイラにやらせている感じ.上の例では結局やっていることは C++ STL の vector と一緒なのだけれど,ちょっと考えれば対した発想でもないような気もするけれど,自分にとってはコロンブスの卵だった.今まで気づかず損をしていたなあ.これは感動した.