出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2021/12/09 03:43 UTC 版)
「多重定義」の記事における「テンプレートと多重定義」の解説
C++の様に多重定義とテンプレートを使用可能な言語では、両方の機能を組み合わせることにより静的な多態を実現することができる。また、PostgreSQLのストアドプロシージャーの様なテンプレートを備えていない言語でも同様の多態を実現できる場合がある。 以下に例を示す。 // 引数valueの符号に応じて、-1, 1または0を返す関数。// ※absを使用しているため、引数に指定できる値の値の範囲はこの関数の引数と同じ型(反変な型)をとるabsの仕様に依存する。template Type Sign( Type const &value ){ return Type() == value ? Type() : abs( value ) / value; // 除算演算子及び、abs関数の実体はSignの引数によって変わる}int main(void){ double value = -2; std::valarray array( 2 ); // ※double用にとのincludeが必要。 double value_sign = Sign( value ); // double型の-1, 1または0が返る std::valarray array_sign = Sign( array ); // -1, 1または0を含むvalarrayの値が返る return EXIT_SUCCESS;} この例ではSign関数内部の演算子とabs関数がSign関数の引数に指定した値によって変化する。なお上記関数テンプレートは複素数型 std::complex に対して適用することもできる。その場合、結果は正規化された複素数(正規化ベクトル)となり、符号関数の複素数への拡張に一致する。 この様にテンプレートと多重定義を備える言語では、多重定義でオーバーライドを代用することができる。 多重定義による多態は、コンパイル時にしか実現できないという問題があるものの単純なオーバーライドでは実現しづらい各種柔軟性を備えている。 まず、メンバー関数だけでなく大域スコープの関数をクラスのインターフェースの一部として見做す事が出来るようになる。これにより、単に手続き型の要素でしか無かった大域スコープの関数をオブジェクト指向機能の一要素として組み入れる事が出来る。そして、大域スコープの関数がインターフェースとして機能し始める事によりクラスだけでなく、intやdouble型といったメンバー関数を持てない型にもオブジェクト指向の恩恵が得られる様になるのである。 次に、大域スコープの関数は直接クラスに所属しないという特性によりメンバー関数より柔軟な拡張性を得る事が出来る。例えば、外部のライブラリーのあるクラスにメンバー関数を追加することは、外部のライブラリーに手を加えなければいけないため事実上無理である。それに対し大域スコープの関数を追加する場合は、外部のライブラリーに手を加える必要がなく容易である。また、関数をテンプレートで実装すれば複数のクラスを横断的に拡張できる。先にテンプレート関数が存在する場合や、拡張対象のクラスの親クラスに対する関数が存在する場合、新たに、より具体的な型を引数に取る関数を追加することで静的なオーバーライドが可能となる。 次に、単一ディスパッチでは不可能な多重ディスパッチを模倣できるという点がある。これにより、例えば矩形を描画しようとする際、描画先デバイスが矩形の描画に対応していれば、デバイスに直接矩形情報を送り、描画先デバイスが矩形描画に対応していなければ、パスや線分等その他の機能を使って矩形を描画するといった処理が自然な形で記述可能となる。 なお大域スコープと記述しているが名前空間の中にあっても次の例のように多態性の実現は可能である。この仕組みを実引数依存の名前探索という。 #include namespace Graphics{ class Line { /* 省略 */ }; class Ellipse { /* 省略 */ }; class Square { /* 省略 */ Line At( size_t index ) const; // 四角形の辺を返す関数 /* 省略 */ }; void Draw( ... ) { } // 四角形を描画する template void Draw( Type &device, Square const &shape ) { Draw( device, shape.At( 0 ) ); Draw( device, shape.At( 1 ) ); Draw( device, shape.At( 2 ) ); Draw( device, shape.At( 3 ) ); }}namespace PNGDevice{ struct Device { /* 省略 */ }; // PNG用に線分を描画する void Draw( Device &device, Graphics::Line const &shape );}namespace JpegDevice{ struct Device { /* 省略 */ }; // Jpeg用に線分を描画する void Draw( Device &device, Graphics::Line const &shape );}int main(){ PNGDevice::Device device0; JpegDevice::Device device1; Draw( device0, Graphics::Square( 0, 0, 1, 1 ) ); // 内部でPNGDevice::Draw( Device &, Graphics::Line const & )を呼び出す Draw( device1, Graphics::Square( 0, 0, 1, 1 ) ); // 内部でJpegDevice::Draw( Device &, Graphics::Line const & )を呼び出す Draw( device0, Graphics::Ellipse( 0, 0, 1, 1 ) ); // Graphics::Draw( ... )を呼び出す Draw( device1, Graphics::Ellipse( 0, 0, 1, 1 ) ); // Graphics::Draw( ... )を呼び出す return EXIT_SUCCESS;}
※この「テンプレートと多重定義」の解説は、「多重定義」の解説の一部です。
「テンプレートと多重定義」を含む「多重定義」の記事については、「多重定義」の概要を参照ください。