C++のテンプレート編集

メイン記事:テンプレート(C++)

C++では、テンプレートを使用してジェネリックプログラミング C++標準ライブラリには、一般的なデータ構造とアルゴリズムのテンプレートのフレームワークを提供する標準テンプレートライブラリ C++のテンプレートは、実行時ではなくコンパイル時にコードの一部を事前評価する方法であるテンプレートメタプログラミングにも使用できます。 利用テンプレートの専門,C++テンプレートとチューリング完了です。,

Technical overviewEdit

関数テンプレートとクラステンプレートの二種類のテンプレートがあります。 関数テンプレートは、インスタンス化時に提供されるパラメータ化型に基づいて通常の関数を作成するためのパター たとえば、C++標準テンプレートライブラリには、xまたはyのいずれか大きい方を返す関数を作成する関数テンプレートmax(x,y)が含まれています。, max()は次のように定義できます。

template <typename T>T max(T x, T y) { return x < y ? y : x;}

この関数テンプレートの特殊化、特定の型を持つインスタンス化は、通常の関数と同じように呼び出すことができます。

std::cout << max(3, 7); // Outputs 7.

コンパイラはmaxを呼び出すために使用される引数を調べ、これがmaxへの呼び出しであると判断します。(int、int)。, 次に、パラメータ化タイプTがintである関数のバージョンをインスタンス化し、次の関数と同等にします。

int max(int x, int y) { return x < y ? y : x;}

これは、引数xとyが整数、文字列、または式x<yが適切であるか、より具体的にはoperatorが定義されています。 共通の継承は、使用できる型のセットには必要ないため、duck typingと非常によく似ています。, カスタムデータ型を定義するプログラムは、演算子のオーバーロードを使用して、その型に対する<の意味を定義できるため、max()関数テンプレー この孤立した例では、これは小さな利点に見えるかもしれませんが、STLのような包括的なライブラリのコンテキストでは、プログラマは新しいデータ型, <を定義するだけで、標準のsort()、stable_sort()、binary_search()アルゴリズムで型を使用したり、セット、ヒープ、連想配列などのデータ構造の中に入れたりできます。

C++テンプレートは、コンパイル時に完全に型セーフです。 デモンストレーションとして、標準型complexは<演算子を定義していません。 したがって、xとyが複素数値の場合、max(x,y)はコンパイルエラーで失敗します。, 同様に、<に依存する他のテンプレートは、(ファンクタまたは関数の形式で)比較が提供されない限り、複雑なデータに適用できません。 たとえば、比較が提供されない限り、複合体をマップのキーとして使用することはできません。 残念ながら、コンパイラは歴史的に、この種のエラーに対してやや難解で、長く、役に立たないエラーメッセージを生成します。 特定のオブジェク <の代わりにcompareを使用する言語でも、複雑な値をキーとして使用できます。,

第二の種類のテンプレートであるクラステンプレートは、同じ概念をクラスに拡張します。 クラステンプレートの特殊化はクラスです。 クラステンプレ たとえば、STLにはリンクリストコンテナがあります。 整数のリンクリストを作成するには、list<int>と書きます。 文字列のリストは、list<string>と表されます。 リストには、互換性のあるパラメータ化タイプに対して機能する標準関数のセットが関連付けられています。,

テンプレートspecializationEdit

C++のテンプレートの強力な機能は、テンプレートの特殊化です。 これにより、インスタンス化されるパラメータ化された型の特定の特性に基づいて代替実装を提供できます。 テンプレートの特殊化には、特定の形式の最適化を可能にすることと、コードの肥大化を減らすことです。

たとえば、sort()テンプレート関数を考えてみましょう。 このような関数が行う主な活動の一つは、コンテナの位置の二つの値を交換または交換することです。, 値が大きい場合(それぞれを格納するのにかかるバイト数に関して)、最初にオブジェクトへのポインタの別のリストを構築し、それらのポインタをソートしてから、最終的なソートされたシーケンスを構築する方が速いことがよくあります。 しかし、値が非常に小さい場合は、通常、必要に応じて値をインプレースで交換するのが最も速いです。 さらに、パラメータ化された型がすでに何らかのポインタ型である場合、別のポインタ配列を構築する必要はありません。, テンプレートの特殊化により、テンプレート作成者は異なる実装を記述し、使用する各実装に対してパラメータ化された型が持つ必要がある特性を指定

関数テンプレートとは異なり、クラステンプレートは部分的に特殊化できます。 と代替のクラステンプレートコードを提供できる場合のテンプレートパラメータが知られているがその他のテンプレートパラメータのクラスを提供します。., これを使用すると、たとえば、パラメータ化型のコピーにコストがかかることを前提とした既定の実装(プライマリ特殊化)を作成し、コピーが安価な型の部分特殊化を作成して、全体的な効率を向上させることができます。 このようなクラステンプレートのクライアントは、コンパイラがそれぞれのケースで一次特化または一部の部分特化を使用しているかどうかを知る, クラステンプレートは完全に特殊化することもでき、すべてのパラメータ化型がわかっている場合に別の実装を提供できることを意味します。

利点と欠点編集

max()関数のようなテンプレートのいくつかの用途は、以前は関数のようなプリプロセッサマクロ(Cプログラミング言語の遺産)によって満たされていた。 たとえば、可能なmax()マクロは次のとおりです。

#define max(a,b) ((a) < (b) ? (b) : (a))

マクロはコンパイル前にプリプロセッサによって展開され、コンパイル時にテンプレートが展開されます。, テンプレートは、コンパイラが適切と判断した場合にインライン関数として展開することもできます。 このように機能すようなマクロ機能テンプレートのない実行時のオーバーヘッド。

ただし、テンプレートは一般的に考えられる改善をもたらすマクロのことを実感させられた。 テンプレートはタイプセーフです テンプレートは、副作用のあるパラメータを二度評価するなど、関数のようなマクロを多用するコードに見られる一般的なエラーのいくつかを避けます。 おそらく最も重要なことは、テンプレートすることをサポートすることは適用が問題によりマクロです。,

テンプレートの使用には、サポートされている機能、コンパイラのサポート、貧弱なエラーメッセージ、およびコードの肥大化があります。

  1. C++のテンプレートには多くの機能が欠けているため、それらを実装して簡単な方法で使用することは不可能であることがよくあります。 代わりにプログラマーに頼っていて複雑な技が肥大化し、理解に苦しく維持す。 C++標準の現在の開発は、これらのトリックを多用し、テンプレート上またはテンプレートを念頭に置いて多くの新機能を構築することによって、この問題をさらに悪化させます。,
  2. 多くのコンパイラは歴史的にテンプレートのサポートが不十分なため、テンプレートを使用すると、コードの移植性がやや低下します。 また、c++コンパイラがC++対応ではないリンカーで使用されている場合、または共有ライブラリの境界を越えてテンプレートを使用しようとしている場 しかし、現代のほとんどのコンパイラはかなり堅牢で標準のテンプレートをサポートしており、新しいC++標準であるC++11はこれらの問題にさらに対処
  3. ほぼすべてのコンパイラを干の混乱をもたらすでしょうが、長くは非協力エラーメッセージがエラーが検出されたコードを使用するテンプレート。, このテンプレートは難しい。
  4. 最後に、テンプレートを使用するには、コンパイラが使用する型パラメータの順列ごとに、テンプレートされたクラスまたは関数の個別のインスタンスを生成する必要があります。 (これは、C++の型がすべて同じサイズではなく、データフィールドのサイズがクラスの仕組みにとって重要であるために必要です。 したがって、テンプレートを無差別に使用すると、コードが膨らみ、実行ファイルが過度に大きくなる可能性があります。, しかし、テンプレートの特殊化と派生を賢明に使用すると、このようなコードの肥大化が劇的に減ることがあります。

テンプレートが使用されているため、派生を使用してコードが複製される問題を減らすことができますか? このような派生するテンプレートから通常のクラスです。 この手法は、実際の使用でのコードの肥大化を抑制することに成功しました。 ない人は技術を駆使したコードの複製でコストメガバイトコードのスペースも緩やかなサイズです。,

-Bjarne Stroustrup,The Design and Evolution of C++,1994

テンプレートによって生成される余分なインスタンス化は、デバッガがテンプレートで正常に動作すること たとえば、ソースファイルからテンプレート内のデバッグブレークポイントを設定すると、目的の実際のインスタンス化でブレークポイントを設定できな,

また、コンパイラはテンプレートのマクロのような展開を実行し、コンパイル時にそれらの異なるインスタンスを生成する必要があるため、テンプレート 分子を鋳型としたクラスや機能を含む多くの標準テンプレートライブラリ(STL),が含まれていない場合ヘッダファイルできませんの取りまとめを行いました。 (これに対し非分子を鋳型としたコードは、デバイナリを提供だけを宣言がヘッダファイルのためのコードを使用しています。, これは、実装コードを公開することによって不利になり、いくつかの抽象化を削除し、クローズドソースプロジェクトでの使用を制限する可能性があります。

DEditのテンプレート

Dプログラミング言語は、C++に基づく設計に基づくテンプレートをサポートしています。,ほとんどのC++テンプレートイディオムは変更なしでDに引き継がれますが、Dはいくつかの追加機能を追加します。

  • Dのテンプレートパラメータは型とプリミティブ値だけに制限されるのではなく、任意のコンパイル時の値(文字列や構造体リテラルなど)や、他のテンプレートやテンプレートのインスタンス化を含む任意の識別子へのエイリアスも許可します。
  • テンプレート制約と静的ifステートメントは、c++の概念と同様に、c++の置換失敗はエラーではありません(SFINAE)メカニズムに代わるものを提供します。
  • これは(…, 現できる投機的なインスタンス生成を検証するオブジェクトの特性でコンパイルす。
  • autoキーワードとtypeof式により、変数宣言と関数の戻り値の型推論が可能になり、”Voldemort型”(グローバル名を持たない型)が許可されます。

DのテンプレートはC++とは異なる構文を使用します:C++ではテンプレートパラメータは角かっこで囲まれています(Template<param1,param2>)、Dは感嘆符とかっこ(param1、param2)。,これにより、比較演算子のあいまいさによるC++の解析の困難を回避できます。パラメータが一つしかない場合は、括弧を省略できます。

従来、Dは上記の機能をコンパイル時の多型を利用特性に基づく汎用プラグインです。,入力範囲のみを受け入れる関数は、上記のテンプレートをテンプレート制約で使用できます。

auto fun(Range)(Range range) if (isInputRange!Range){ // ...}
Code generationEdit

テンプレートのメタプログラミングに加えて、Dはコンパイル時のコード生成を有効にするいくつかの機能も提供します。

auto fun(Range)(Range range) if (isInputRange!Range){ // ...}

Dは、テンプレートのメタプログラミングに加えて、コンパイル時のコード生成を有効にするためのいくつかの機能も提供します。

  • import式では、ディスクからファイルを読み取り、その内容を文字列式として使用できます。,
  • コンパイル時のリフレクションでは、コンパイル中に宣言とそのメンバーを列挙および検査できます。
  • ユーザー定義属性を使用すると、ユーザーは任意の識別子を宣言に添付できます。
  • コンパイル時関数実行(CTFE)では、Dのサブセット(安全な操作に制限されている)をコンパイル中に解釈できます。
  • String mixinを使用すると、文字列式の内容をプログラムの一部となるDコードとして評価およびコンパイルできます。,

上記を組み合わせることで、既存の宣言に基づいてコードを生成できます。たとえば、D直列化フレームワークでは、型のメンバーを列挙し、直列化された型ごとに特殊な関数を生成して、直列化と逆直列化を実行できます。ユーザ定義の属性が更に直列化して表示します

インポート式とコンパイル時の関数の実行により、ドメイン固有の言語を効率的に実装することもできます。,たとえば、HTMLテンプレートを含む文字列を受け取り、同等のDソースコードを返す関数があれば、次のように使用できます。

EiffelEditのジェネリック性

ジェネリッククラスは、元のメソッドと言語の設計以来、Eiffelの一部でした。 財団の刊行物のエッフェル、使用の期genericityの作成と使用の汎用。

Basic/Unconstrained genericityEdit

ジェネリッククラスは、クラス名と一つ以上の正式なジェネリックパラメータのリストで宣言されます。, 次のコードでは、クラスLISTには正式なジェネリックパラメータG

正式なジェネリックパラメータは、ACCOUNTDEPOSITが他のクラス名であるように、ジェネリッククラスの宣言が行われたときに提供される任意のクラス名のプレースホルダである。—– ACCOUNTおよびDEPOSITは、実際の使用でGを置き換える実際のクラス名を提供するため、実際の汎用パラメータと見なされます。,

 list_of_accounts: LIST -- Account list list_of_deposits: LIST -- Deposit list

エッフェル型システム内では、classLIST はクラスと見なされますが、型とは見なされません。 ただし、LIST のようなLIST の一般的な派生は型と見なされます。

Constrained genericityEdit

上記のリストクラスでは、Gに代わる実際のジェネリックパラメータを他の利用可能なクラスにすることができます。, 有効な実際のジェネリックパラメーターを選択できるクラスのセットを制約するには、ジェネリック制約を指定できます。 以下のclassSORTED_LIST宣言では、ジェネリック制約は、有効な実際のジェネリックパラメータがclassCOMPARABLEから継承するクラス ジェネリック制約により、SORTED_LIST要素が実際にソートできるようになります。,

class SORTED_LIST 

JavaEditのジェネリック

メイン記事:Javaのジェネリック

ジェネリック、つまり”containers-of-type-T”のサポートは、2004年にJ2SE5.0の一部としてJavaプログラミング言語に追加されました。 Javaでは、ジェネリックはコンパイル時にのみ型の正確性がチェックされます。 ジェネリック型情報は、古いJVM実装との互換性を維持するために、型消去と呼ばれるプロセスを介して削除され、実行時に使用できなくなります。, たとえば、List<String>は生のタイプのListに変換されます。 コンパイラは、要素がリストから取得されるときに文字列型に変換する型キャストを挿入し、C++テンプレートなどの他の実装と比較してパフォーマンス

.Net Editのジェネリック

ジェネリックは、.NET Framework2.0の一部として2005年に追加され、1999年に開始されたMicrosoft Researchの研究プロトタイプに基づいています。 Javaのジェネリックに似ていますが、。,NETジェネリックは型消去を適用しませんが、reificationを使用して実行時にファーストクラスのメカニズムとしてジェネリックを実装します。 この設計上の選択により、ジェネリック型の保存によるリフレクションを許可するとともに、消去の制限(ジェネリック配列を作成できないなど)を軽減するなど、追加の機能が提供されます。 これは、ランタイムキャストや通常は高価なボクシング変換からのパフォーマンスヒットがないこ, がプリミティブ値型として使用され一般の引数は、専門の実装を効率的に汎用集ます。 C++およびJavaと同様に、Dictionary<string、List<int>>などのネストされたジェネリック型は有効な型ですが、コード分析設計規則のメンバー署名については無効にすることをお勧めします。

。,NETでは、ジェネリック型を値型に制限する、クラスに制限する、コンストラクタを持つ、インタフェースを実装するなど、whereキーワードを使用してジェネリック型制 このメソッドの型制約は、このメソッドが汎用IComparable<T>インターフェイスを実装する任意の型Tに適用できることを示します。, これにより、コンパイル時にエラーの場合、呼び出された場合のタイプはサポートしない。 このインターフェイスは、汎用メソッドCompareTo(T)を提供します。

上記のメソッドは、単に非ジェネリック配列型を使用して、ジェネリック型なしで記述することもできます。 しかし、配列は反変であるため、キャストは型セーフではなく、コンパイラはジェネリック型を使用するときにキャッチされる可能性のあるエラーを見つけることができません。 また、メソッドへのアクセスを必要とされる配列内の項目オブジェクトとしてではなく、必要と鋳造の比較には、つの要素からなる。, (Intなどの型のような値型の場合、これはボクシング変換が必要ですが、これはComparer<T>標準のコレクションクラスで行われているように、クラスを使用して回避することができます。ジェネリック.netクラスの静的メンバーの注目すべき動作は、実行時の型ごとの静的メンバーのインスタンス化です(以下の例を参照)。

DelphiEditのジェネリック

DelphiのObject Pascal方言は、Delphi2007リリースでジェネリックを取得しました。,Delphi2009リリースのネイティブコードに追加される前のNETコンパイラ。 Delphiジェネリックのセマンティクスと機能は、.NET2.0のジェネリックによって持っていたものを主にモデル化されていますが、実装は必然的に全く異 ここでは、上記の最初のC#の例の多かれ少なかれ直接翻訳です:

C#と同様に、メソッドだけでなく、全体の型は、一つ以上の型パラメータを持つことがで この例では、TArrayはジェネリック型(言語によって定義されています)であり、MakeAtLeastはジェネリックメソッドです。, 使用可能な制約は、任意の値型、任意のクラス、特定のクラスまたはインターフェイス、およびパラメーターなしコンストラクターを持つクラスなど、C#で使用可能な制 複数の制約は、加法的和集合として機能します。

Free Pascalにおけるジェネリック性

Free PascalはDelphiの前にジェネリックを実装し、異なる構文とセマンティクスを持っていました。 ただし、FPCバージョン2.6.0以降では、{$mode Delphi}言語モードを使用する場合、Delphiスタイルの構文が使用できます。 したがって、自由なPascalプログラマは、どのスタイルでもジェネリックを使用できます。,

DelphiとFree Pascalの例:

関数型言語編集

HaskellEditのジェネリック性

Haskellの型クラスメカニズムはジェネリックプログラミングをサポートしています。Haskellの定義済み型クラスの六つ(Eq、等価性のために比較できる型、およびShow、値を文字列としてレンダリングできる型を含む)は、派生インスタンスをサポートする特別なプロパティを持っています。, つまり、新しい型を定義するプログラマは、クラスインスタンスを宣言するときに通常必要なクラスメソッドの実装を提供せずに、この型がこれらの 必要なすべてのメソッドは、型の構造に基づいて自動的に構築される”派生”されます。,

data BinTree a = Leaf a | Node (BinTree a) a (BinTree a) deriving (Eq, Show)

これにより、等価関数(==)と文字列表現関数(show)が、t自身がこれらの操作をサポートしている場合、bintree T形式の任意の型に対して自動的に定義されます。,

EqとShowの派生インスタンスのサポートは、それらのメソッド==とshowをパラメトリックな多態性関数とは質的に異なる方法で汎用にします:これらの”関数”(より正確には、型インデックス付き関数のファミリー)は、さまざまな型の値に適用でき、引数の型ごとに異なる動作をするが、新しい型のサポートを追加するための作業はほとんど必要ではありません。 Ralf Hinze(2004)は、特定のプログラミング技術によってユーザ定義型クラスに対して同様の効果が達成できることを示している。, 他の研究者は、Haskellの文脈とHaskellの拡張(後述)において、これおよび他の種類のジェネリック性へのアプローチを提案している。

PolyPEdit

PolyPはHaskellの最初の汎用プログラミング言語拡張であった。 PolyPでは、汎関数はpolytypicと呼ばれます。 この言語では、通常のデータ型のパターンファンクタの構造上の構造的誘導によって、そのような多型関数を定義できる特別な構造を導入しています。 PolyPの通常のデータ型は、Haskellのデータ型のサブセットです。, 通常のデータ型tは*→*の種類でなければならず、aが定義内の仮型引数である場合、tへのすべての再帰呼び出しはt aの形式を持つ必要があります。PolyPのflatten関数はここで例として提供されています:

Generic HaskellEdit

Generic Haskellは、オランダのユトレヒト大学で開発されたHaskellの別の拡張です。, それが提供する拡張は次のとおりです。

  • 型インデックス付きの値は、Haskellのさまざまな型コンストラクタ(単位型、プリミティブ型、合計、積、およびユーザ定義型コンストラクタ)にインデックス付きの値として定義されます。 さらに、constructor casesを使用して特定のコンストラクタに対する型インデックス付き値の動作を指定し、default casesを使用して別のジェネリック定義を再利用することもできます。

結果の型インデックス付き値は、任意の型に特化することができます。

  • 種類インデックス付き型は、*とk→k’の両方にケースを与えることによって定義された、種類に対してインデックス付き型です。, インスタ
  • ジェネリック定義は、型または種類に適用することによって使用できます。 これはgeneric applicationと呼ばれます。 結果は、どの種類のジェネリック定義が適用されるかに応じて、型または値になります。
  • 汎用抽象化は、(特定の種類の)型パラメータを抽象化することによって、汎用定義を定義できるようにします。
  • 型インデックス付き型は、型コンストラクターに対してインデックス付けされる型です。 これらは、より複雑な汎用値に型を与えるために使用できます。, 結果の型インデックス付き型は、任意の型に特化することができます。

例として、ジェネリックHaskellの等価関数は次のとおりです。

CleanEdit

Cleanは、ジェネリックプログラミングベースのPolyPと、GHC>=6.0でサポートされているジェネリックHaskellを提供します。 でparametrizesようとしてもオーバーロード.

その他の言語編集

MLファミリーのプログラミング言語は、パラメトリックポリモーフィズムとファンクタと呼ばれるジェネリックモジュール,標準MLとOCamlの両方がファンクタを提供しており、これはクラステンプレートやAdaの汎用パッケージに似ています。 Scheme構文抽象はまた、ジェネリシティとの関連を持っています–これらは実際にはc++からのテンプレートのスーパーセットです。

Verilogモジュールは、モジュールのインスタンス化時に実際の値が割り当てられる一つ以上のパラメータを取ることができます。 一つの例は、配列の幅がパラメータを介して与えられる一般的なレジスタ配列です。, このような配列は、汎用ワイヤベクトルと組み合わせて、単一のモジュール実装から任意のビット幅を持つ汎用バッファまたはメモリモジュールを作

Adaから派生したVHDLも汎用的な機能を持っています。