ジェネリックプログラミング

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

ジェネリックプログラミングは、アルゴリズムが後で指定されるタイプに関して記述され、パラメーターとして提供される特定のタイプに対して必要なときにインスタンスされるコンピュータープログラミングのスタイルです1973年にMLプログラミング言語によって開拓されたこのアプローチ[1] [2]は、使用時に動作するタイプのセットのみが異なる共通の関数またはタイプを記述できるため、重複を減らすことができます。このようなソフトウェアエンティティはAdaC#ではジェネリックとして知られています。 DelphiEiffelF#JavaNimPythonRustSwiftTypeScriptVisual Basic.NETそれらはMLScalaJulia、およびHaskellではパラメトリック多型として知られています(Haskellコミュニティでは、関連するが多少異なる概念に対して「ジェネリック」という用語も使用されています)。C ++およびDテンプレート; 影響力のある1994年の本のパラメータ化されデザインパターン[3]

「ジェネリックプログラミング」という用語は、元々、DavidMusserAlexanderStepanov [4]によって、上記よりも具体的な意味で造られました。これは、型の基本的な要件がアルゴリズムとデータ構造の具体的な例全体から抽象化され、形式化されるプログラミングパラダイムを表すためです。概念として、これらの概念の観点から実装されたジェネリック関数を使用し、通常は上記の言語ジェネリックメカニズムを使用します。

Stepanov–Musserおよびその他のジェネリックプログラミングパラダイム

ジェネリックプログラミングは、Musser&Stepanov(1989)で次のように 定義されています。

ジェネリックプログラミングは、具体的で効率的なアルゴリズムから抽象化して、さまざまなデータ表現と組み合わせてさまざまな有用なソフトウェアを作成できるジェネリックアルゴリズムを取得するというアイデアを中心としています。

—  Musser、David R。; ステパノフ、アレクサンダーA.、ジェネリックプログラミング[5]

「ジェネリックプログラミング」パラダイムは、ソフトウェア分解へのアプローチであり、抽象代数における代数理論の抽象化と同様に、型に関する基本的な要件がアルゴリズムとデータ構造の具体的な例全体から抽象化され、概念として形式化されます。[6]このプログラミングアプローチの初期の例は、SchemeとAda [7]で実装されましたが、最もよく知られている例は、分離に使用されるイテレータの理論を開発したStandard Template Library(STL)[8] [9]です。シーケンスデータ構造とそれらを操作するアルゴリズム。

たとえば、N個のシーケンスデータ構造(たとえば、単一リンクリスト、ベクトルなど)と、それらを操作するMfind個のアルゴリズム(たとえば、sortなど)が与えられた場合、直接的なアプローチでは、各データ構造に固有の各アルゴリズムを実装し、N × Mの組み合わせを提供します。埋め込む。ただし、ジェネリックプログラミングのアプローチでは、各データ構造はイテレータの概念のモデル(現在の値を取得するために参照を解除したり、シーケンス内の別の値を指すように変更したりできる単純な値の型)を返し、代わりに各アルゴリズムを記述します。一般的に、そのようなイテレータの引数を使用します。たとえば、サブシーケンスまたは範囲の開始と終了を指すイテレータのペアプロセスへ。したがって、N + Mのデータ構造とアルゴリズムの組み合わせのみを実装する必要があります。いくつかのイテレータの概念がSTLで指定されており、それぞれがより制限的な概念の改良版です。たとえば、フォワードイテレータは、シーケンス内の次の値への移動のみを提供します(たとえば、単一リンクリストまたは入力データのストリームに適しています)が、ランダムアクセスイテレータは、シーケンスの任意の要素への直接の一定時間アクセスも提供します(たとえば、ベクトルに適しています)。重要な点は、データ構造が、効率的に実装できる最も一般的な概念のモデル、つまり計算の複雑さを返すことです。要件は、明示的に概念定義の一部です。これにより、特定のアルゴリズムを適用できるデータ構造が制限され、そのような複雑さの要件がデータ構造の選択の主要な決定要因になります。ジェネリックプログラミングは、グラフアルゴリズムなど、他のドメインでも同様に適用されています。[10]

このアプローチは、コンパイル時の汎用性/テンプレートの言語機能を利用することがよくありますが、実際には特定の言語技術の詳細とは無関係であることに注意してください。ジェネリックプログラミングのパイオニアであるアレクサンダーステパノフは次のように書いています。

ジェネリックプログラミングとは、アルゴリズムとデータ構造を抽象化して分類することです。型理論からではなく、クヌースからインスピレーションを得ています。その目標は、有用で効率的で抽象的なアルゴリズムとデータ構造の体系的なカタログを段階的に構築することです。そのような事業はまだ夢です。

—  Alexander Stepanov、STLの短い歴史[11] [12]

イテレータ理論は、リングバナッハ空間の理論が数学の中心であるのと同じくらい、コンピュータサイエンスの中心であると私は信じています。

— アレクサンダー・ステパノフ、A。ステパノフへのインタビュー[13]

Bjarne Stroustrupは、 次のように述べています。

Stepanovに続いて、言語機能に言及することなくジェネリックプログラミングを定義できます。具体的な例から最も一般的で抽象的な形式にアルゴリズムとデータ構造を持ち上げます。

—  Bjarne Stroustrup、現実の世界で、そして現実の世界のために言語を進化させる:C ++ 1991-2006 [12]

ジェネリックプログラミングとして説明されている他のプログラミングパラダイムには、「ジェネリックプログラミング–はじめに」で説明されているデータ型ジェネリックプログラミングが含まれます。[14]ボイラープレートスクラップアプローチは、Haskellの軽量ジェネリックプログラミングアプローチです。[15]

この記事では、上記ジェネリックプログラミングの高レベルのプログラミングパラダイムを、それらを実装するために使用される低レベルのプログラミング言語のジェネリックメカニズムと区別します(ジェネリックのプログラミング言語のサポートを参照)。ジェネリックプログラミングパラダイムの詳細と比較については、を参照してください。[16]

汎用性のためのプログラミング言語のサポート

ジェネリック機能は、少なくとも1970年代以降、 MLCLUAdaなどの言語で高級言語に存在し、その後、 BETAC ++DEiffelJavaなどの多くのオブジェクトベースおよびオブジェクト指向言語で採用されました。そして、DECは現在は機能していないTrellis-Owl言語です。

Genericityは、さまざまなプログラミング言語で異なる方法で実装およびサポートされます。「ジェネリック」という用語は、さまざまなプログラミングコンテキストでも異なる方法で使用されています。たとえば、Forthでは、コンパイラはコンパイル中にコードを実行でき、新しいコンパイラキーワードとそれらの単語の新しい実装をその場で作成できます。コンパイラの動作を明らかにする単語はほとんどないため、当然、一般的な機能を提供しますが、ほとんどのForthテキストではそのように言及されていません。同様に、動的型付け言語、特にインタプリタ言語は、通常、汎用性を提供しますデフォルトでは、関数への値の受け渡しと値の割り当てはどちらも型に依存せず、そのような動作は抽象化やコードの簡潔さのために利用されることがよくありますが、言語で採用されている動的型付けシステムの直接の結果であるため、これは通常、汎用性とはラベル付けされません。[要出典]この用語は、関数型プログラミング、特にHaskellのような言語で使用されています。この言語では、型が常にパラメトリックであり、それらの型の実際のコードが一般的である構造型システムを使用します。これらの使用法は、コードの保存と抽象化のレンダリングという同様の目的を果たします。

配列構造体は、事前定義されたジェネリック型として表示できます。配列または構造体型を使用するたびに、新しい具象型がインスタンス化されるか、以前にインスタンス化された型が再利用されます。配列要素型と構造体要素型はパラメーター化された型であり、対応するジェネリック型をインスタンス化するために使用されます。これは通常、コンパイラに組み込まれており、構文は他の一般的な構造とは異なります。一部の拡張可能なプログラミング言語は、組み込み型とユーザー定義型のジェネリック型を統合しようとします。

プログラミング言語の一般性メカニズムの広範な調査は次のとおりです。ジェネリックプログラミングのメカニズムの適合性を比較する特定の調査については、を参照してください。[17]

オブジェクト指向言語の場合

静的に型付けされた言語でコンテナクラスを作成する場合、特に各データ型のコードが実質的に同一である場合は、含まれるデータ型ごとに特定の実装を作成するのは不便です。たとえば、C ++では、クラステンプレートを定義することで、このコードの重複を回避できます。

テンプレート<タイプ名 T >
クラス リスト{ 
  //クラスの内容。
};

リスト<動物> list_of_animals ; 
リスト<> list_of_cars ; 

上記Tは、リストの作成時に指定されたタイプのプレースホルダーです。これらの「containers-of-type-T」は、一般にテンプレートと呼ばれ、サブタイプ署名などの特定のコントラクトが保持されている限り、クラスをさまざまなデータ型で再利用できます。この一般性メカニズムは、交換可能なサブクラスのアルゴリズム的な使用法である包含ポリモーフィズムと混同しないでください。たとえば、タイプとのオブジェクトを含むタイプのオブジェクトのリストですテンプレートは、以下の例 のように、タイプに依存しない関数にも使用できます。Moving_ObjectAnimalCarSwap

//「&」は参照
テンプレート<タイプ名 T >を示します
void Swap T a T b { //同様ですが、より安全で潜在的に高速な関数//は標準ライブラリヘッダーで定義されています<utility> T temp = b ;      
                        
     
  b = a ;  
  a = temp ;  
}

std :: string world = "World!" ;   
std :: string hello = "Hello、" ;   
スワップ世界こんにちは); 
std :: cout << world << hello << '\ n ' ; //出力は「Hello、World!」です。        

上記で使用されているC ++構造は、プログラマーや言語設計者の間で概念を普及させ、多くのジェネリックプログラミングイディオムをサポートするジェネリック構造としてtemplate広く引用されています[要出典] 。Dプログラミング言語は、C ++の前例に基づいて、構文が単純化された、完全に汎用的なテンプレートも提供します。Javaプログラミング言語は、 J2SE 5.0 の導入以来、C ++に基づいて構文的に汎用機能を提供してきました。

C# 2.0、Oxygene 1.5(Chromeとも呼ばれます)、およびVisual Basic .NET 2005には、バージョン2.0以降 のMicrosoft .NETFrameworkに存在するジェネリックスのサポートを利用する構造があります。

エイダのジェネリック

Adaは、1977年から1980年に最初に設計されて以来、ジェネリック医薬品を使用してきました。標準ライブラリはジェネリックスを使用して多くのサービスを提供します。Ada 2005は、C ++の標準テンプレートライブラリに触発された包括的なジェネリックコンテナライブラリを標準ライブラリに追加します

ジェネリックユニットは、 1つ以上のジェネリック仮パラメーターを受け取るパッケージまたはサブプログラムです。

ジェネリック仮パラメーターは、値、変数、定数、型、サブプログラム、または別の指定されたジェネリックユニットのインスタンスです。ジェネリックの仮型の場合、構文は、離散型、浮動小数点型、固定小数点型、アクセス(ポインター)型などを区別します。一部の仮パラメーターは、デフォルト値を持つことができます。

ジェネリックユニットインスタンス化するために、プログラマーは各フォーマルの実際のパラメーターを渡します。ジェネリックインスタンスは、他のユニットと同じように動作します。実行時に、たとえばループ内で 汎用ユニットをインスタンス化することができます。

汎用パッケージの仕様:

 ジェネリック
    Max_Size   ナチュラル;  -一般的な正式な値
     Element_Type  privateです;  -一般的な正式な型。
 無制限のタイプのパッケージ を受け入れます。スタック 
    タイプです 。Size_Type  範囲 0 です。..  Max_Size ; 
    タイプ スタック  プライベートに制限されてい ます; プロシージャCreate S out Stack ; Initial_Size in Size_Type := Max_Size ); 手順
        
                          
     Push  Into  in  out  Stack ;  Element  in  Element_Type ); 
    プロシージャ ポップ From  in  out  Stack ;  Element  out  Element_Type ); 
    オーバーフロー  例外; 
    アンダーフロー  例外; 
 プライベート
    サブタイプ Index_Type  Size_Type 範囲 1 です ..Max_Size ; 
    タイプ Vector  配列です Index_Type  range  <>) of  Element_Type ; 
    タイプ スタック Allocated_Size  Size_Type  :=  0   レコード
       トップ  Index_Type ; 
       ストレージ  Vector  1  ..  Allocated_Size ); 
    終了レコード; 
 スタックを終了し ます;

汎用パッケージのインスタンス化:

 タイプ Bookmark_Type  新しい Naturalです; 
 -編集中のテキストドキュメント内の場所を記録します

 パッケージ Bookmark_Stacks は新しい スタックです Max_Size  => 20 
                                        Element_Type  => Newsletter_Type ); 
 -ユーザーがドキュメントに記録された場所間をジャンプできるようにします

汎用パッケージのインスタンスを使用する:

 タイプ Document_Type  レコードです
    内容  Ada 文字列無制限Unbounded_String ; 
    ブックマーク  Bookmark_Stacks スタック; 
 終了レコード;

 プロシージャ Edit  Document_Name  in  String  is 
   Document   Document_Type ; 
 begin-
   ブックマークのスタックを初期化します:
   Bookmark_Stacks 作成 S  => ドキュメントブックマーク Initial_Size  =>  10 ); 
   -ここで、ファイルDocument_Nameを開き、それを読み込みます... 
 end  Edit ;
長所と制限

言語構文により、一般的な仮パラメーターの制約を正確に指定できます。たとえば、汎用の正式な型が実際の型としてのみモジュラー型を受け入れるように指定することができます。一般的な仮パラメータ間の制約を表現することも可能です。例えば:

 ジェネリック
     Index_Type  (<>);  -ディスクリートタイプ
    タイプである必要が ありますElement_Typeis  private ; -任意の無制限の型タイプにすることができますArray_TypeElement_Typeの配列Index_Type範囲<>)です;  
            

この例では、Array_TypeはIndex_TypeとElement_Typeの両方によって制約されています。ユニットをインスタンス化するとき、プログラマーはこれらの制約を満たす実際の配列型を渡す必要があります。

このきめ細かい制御の欠点は構文が複雑なことですが、すべてのジェネリック仮パラメーターが仕様で完全に定義されているため、コンパイラーはジェネリックの本体を見なくてもジェネリックをインスタンス化できます。

C ++とは異なり、Adaは特殊なジェネリックインスタンスを許可せず、すべてのジェネリックを明示的にインスタンス化する必要があります。これらのルールにはいくつかの結果があります。

  • コンパイラーは共有ジェネリックスを実装できます。ジェネリックスユニットのオブジェクトコードは、すべてのインスタンス間で共有できます(もちろん、プログラマーがサブプログラムのインライン化を要求しない限り)。さらなる結果として:
    • コードの膨張の可能性はありません(コードの膨張はC ++で一般的であり、以下で説明するように特別な注意が必要です)。
    • 新しいインスタンスに新しいオブジェクトコードは必要ないため、コンパイル時だけでなく実行時にジェネリックをインスタンス化することができます。
    • ジェネリックフォーマルオブジェクトに対応する実際のオブジェクトは、ジェネリック内では常に非静的であると見なされます。詳細と結果については、ウィキブックスの一般的な正式なオブジェクトを参照してください。
  • ジェネリックのすべてのインスタンスはまったく同じであるため、他の人が作成したプログラムを確認して理解するのが簡単です。考慮すべき「特別な場合」はありません。
  • すべてのインスタンス化は明示的であり、プログラムの理解を困難にする可能性のある隠れたインスタンス化はありません。
  • Adaは、特殊化を許可していないため、「テンプレートメタプログラミング」を許可していません。

C ++のテンプレート

C ++は、テンプレートを使用してジェネリックプログラミング手法を有効にします。C ++標準ライブラリには、一般的なデータ構造とアルゴリズムのテンプレートのフレームワークを提供する標準テンプレートライブラリまたはSTLが含まれています。C ++のテンプレートは、テンプレートメタプログラミングにも使用できます。これは、実行時ではなくコンパイル時にコードの一部を事前評価する方法ですテンプレートの特殊化を使用すると、C ++テンプレートはチューリング完全と見なされます。

技術概要

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

テンプレート<タイプ名 T >
T max T x T y {     
  x < yを返しますか?y x ;       
}

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

std :: cout << max 3、7 ; _ //出力7。     

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

int max int x int y {     
  x < yを返しますか?y x ;       
}

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

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

別の種類のテンプレートであるクラステンプレートは、同じ概念をクラスに拡張します。クラステンプレートの特殊化はクラスです。クラステンプレートは、ジェネリックコンテナを作成するためによく使用されます。たとえば、STLにはリンクリストコンテナがあります。整数のリンクリストを作成するには、を書き込みますlist<int>文字列のリストはで示されlist<string>ます。Alistには、互換性のあるパラメータ化タイプで機能する一連の標準関数が関連付けられています。

テンプレートの特殊化

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

たとえば、sort()テンプレート関数。このような関数が行う主なアクティビティの1つは、コンテナの2つの位置で値を交換または交換することです。値が大きい場合(各値を格納するのに必要なバイト数の観点から)、最初にオブジェクトへのポインターの個別のリストを作成し、それらのポインターを並べ替えてから、最終的に並べ替えられたシーケンスを作成する方が速いことがよくあります。 。ただし、値が非常に小さい場合は、通常、必要に応じて値をインプレースで交換するのが最も高速です。さらに、パラメーター化された型がすでに何らかのポインター型である場合は、別個のポインター配列を作成する必要はありません。テンプレートの特殊化により、テンプレートの作成者はさまざまな実装を記述し、使用する実装ごとにパラメーター化された型が持つ必要のある特性を指定できます。

関数テンプレートとは異なり、クラステンプレートは部分的に特殊化できます。つまり、一部のテンプレートパラメータがわかっている場合は、クラステンプレートコードの代替バージョンを提供できますが、他のテンプレートパラメータはジェネリックのままです。これを使用して、たとえば、パラメータ化タイプのコピーにコストがかかると想定するデフォルトの実装(プライマリスペシャライゼーション)を作成し、コピーが安価なタイプの部分的なスペシャライゼーションを作成して、全体的な効率を高めることができます。このようなクラステンプレートのクライアントは、コンパイラがそれぞれの場合にプライマリスペシャライゼーションを使用したのか、部分的なスペシャライゼーションを使用したのかを知る必要なしに、そのスペシャライゼーションを使用するだけです。クラステンプレートは、完全に特殊化することもできます。これは、すべてのパラメーター化タイプがわかっている場合に、代替実装を提供できることを意味します。

長所と短所

関数などのテンプレートの一部の使用法はmax()、以前は関数のようなプリプロセッサ マクロCプログラミング言語のレガシー)で埋められていました。たとえば、このようなマクロの可能な実装は次のとおりです。

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

マクロは、適切にコンパイルされる前に、プリプロセッサによって展開(コピー貼り付け)されます。テンプレートは実際の実際の関数です。マクロは常にインライン展開されます。テンプレートは、コンパイラーが適切であると判断した場合、 インライン関数にすることもできます。

ただし、これらの目的では、テンプレートは一般にマクロよりも優れていると見なされます。テンプレートはタイプセーフです。テンプレートは、副作用のあるパラメーターを2回評価するなど、関数のようなマクロを多用するコードに見られる一般的なエラーの一部を回避します。おそらく最も重要なことは、テンプレートはマクロよりもはるかに大きな問題に適用できるように設計されていることです。

テンプレートの使用には4つの主な欠点があります。サポートされている機能、コンパイラのサポート、不十分なエラーメッセージ(通常はC ++ 20より前のSFINAE)、およびコードの膨張です。

  1. C ++のテンプレートには多くの機能がないため、テンプレートを実装して簡単な方法で使用することはほとんど不可能です。代わりに、プログラマーは複雑なトリックに頼らなければならず、それは肥大化し、理解しにくく、コードを維持するのが困難になります。C ++標準の現在の開発では、これらのトリックを多用し、テンプレート上またはそれらを念頭に置いてテンプレート用の多くの新機能を構築することにより、この問題を悪化させています。
  2. 多くのコンパイラはこれまでテンプレートのサポートが不十分であったため、テンプレートを使用するとコードの移植性が多少低下する可能性がありました。C ++コンパイラがC ++に対応していないリンカで使用されている場合、または共有ライブラリの境界を越えてテンプレートを使用しようとしている場合も、サポートが不十分になる可能性があります。
  3. SFINAEを使用するコードでエラーが検出されると、コンパイラーは、紛らわしく、長く、場合によっては役に立たないエラーメッセージを生成する可能性があります。[18]これにより、テンプレートの開発が困難になる可能性があります。
  4. 最後に、テンプレートを使用するには、コンパイラーが、使用される型パラメーターの順列ごとに、テンプレート化されたクラスまたは関数の個別のインスタンスを生成する必要があります。(C ++の型はすべて同じサイズではなく、データフィールドのサイズはクラスの動作にとって重要であるため、これが必要です。)したがって、テンプレートを無差別に使用すると、コードが膨張し、実行可能ファイルが非常に大きくなる可能性があります。ただし、テンプレートの特殊化と派生を適切に使用すると、場合によっては、このようなコードの膨張を劇的に減らすことができます。

では、テンプレートが使用されているためにコードが複製される問題を減らすために、派生を使用できますか?これには、通常のクラスからテンプレートを導出することが含まれます。この手法は、実際の使用でコードの膨張を抑えるのに成功したことが証明されました。このような手法を使用しない人は、中程度のサイズのプログラムでも、複製されたコードに数メガバイトのコードスペースがかかる可能性があることを発見しました。

—  Bjarne Stroustrup、The Design and Evolution of C ++、1994 [19]

テンプレートによって生成された余分なインスタンス化により、一部のデバッガーがテンプレートを適切に操作するのが困難になる場合もあります。たとえば、ソースファイルからテンプレート内にデバッグブレークポイントを設定すると、必要な実際のインスタンス化でブレークポイントを設定できなかったり、テンプレートがインスタンス化されるすべての場所にブレークポイントを設定したりする可能性があります。

また、テンプレートの実装ソースコードは、それを使用する翻訳ユニット(ソースファイル)が完全に利用可能(ヘッダーに含まれているなど)である必要があります。標準ライブラリの多くを含むテンプレートは、ヘッダーファイルに含まれていない場合、コンパイルできません。(これは、バイナリにコンパイルされ、それを使用するコードの宣言ヘッダーファイルのみを提供するテンプレート化されていないコードとは対照的です。)これは、実装コードを公開することで不利になる可能性があります。クローズドソースプロジェクトでの使用。[要出典]

Dのテンプレート

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

  • Dのテンプレートパラメータは、型とプリミティブ値(C ++ 20より前のC ++の場合のように)だけでなく、任意のコンパイル時の値(文字列や構造体リテラルなど)、および任意の識別子へのエイリアスを許可します。他のテンプレートまたはテンプレートのインスタンス化。
  • テンプレート制約とステートメントは、それぞれC ++のC ++の概念static ifの代替手段を提供します。if constexpr
  • このis(...)式を使用すると、投機的なインスタンス化により、コンパイル時にオブジェクトの特性を検証できます。
  • autoキーワードとtypeof式により、変数宣言と関数の戻り値型推論が可能になり、「Voldemort型」(グローバル名を持たない型)が可能になります。[20]

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

従来、Dは上記の機能を組み合わせて、特性ベースのジェネリックプログラミングを使用したコンパイル時のポリモーフィズムを提供します。たとえば、入力範囲は、によって実行されるチェックを満たす任意のタイプとしてisInputRange定義され、次のように定義されます。

template  isInputRange R 
{ 
    enum  bool  isInputRange  =  is typeof 
    inout  int  =  0 
    { 
        R  r  =  R .init ; //範囲オブジェクトを定義できますif r .empty { } //空のrをテストできます.popFront (); // popFront()を呼び出すことができますauto h = r .front ;     
             
             
            //範囲の先頭を取得できます
    })); 
}

入力範囲のみを受け入れる関数は、テンプレート制約で上記のテンプレートを使用できます。

auto  fun Range )(Range  range 
    if  isInputRange Range 
{ 
    // ... 
}
コード生成

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

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

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

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

//example.httの内容を文字列マニフェスト定数としてインポートします。
enum  htmlTemplate  =  import "example.htt" );

// HTMLテンプレートをDコードにトランスパイルします。
列挙 型htmlDCode  =  htmlTemplateToD htmlTemplate );

// htmlDCodeの内容をDコードとして貼り付けます。
ミックスインhtmlDCode );

エッフェルの寛大さ

ジェネリッククラスは、元のメソッドと言語設計以来、Eiffelの一部となっています。Eiffelの基礎出版物[21] [22]は、ジェネリッククラスの作成と使用を説明するために ジェネリックという用語を使用しています。

基本的/制約のない一般性

ジェネリッククラスは、クラス名と1つ以上の正式なジェネリックパラメーターのリストを使用して宣言されます。次のコードでは、クラスLISTに1つの正式なジェネリックパラメーターがありますG

class 
    LIST  [ G ] 
            ...
機能   -アクセス
    アイテム G-
            カーソルが現在指しているアイテム
            ...
機能   -要素の変更
    put  new_item  G 
            -リストの最後に `new_item 'を追加します
            ..。。

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

    list_of_accounts  LIST  [ ACCOUNT ] 
            -アカウントリスト

    list_of_deposits  LIST  [ DEPOSIT ] 
            -デポジットリスト

Eiffel型システム内では、クラスはクラスLIST [G]と見なされますが、型とは見なされません。LIST [G]ただし、などの一般的な派生はLIST [ACCOUNT]タイプと見なされます。

制約された一般性

上記のリストクラスの場合、代わりとなる実際のジェネリックパラメーターGは他の使用可能なクラスにすることができます。有効な実際のジェネリックパラメーターを選択できるクラスのセットを制約するために、ジェネリック制約を指定できます。以下のクラスの宣言ではSORTED_LIST、ジェネリック制約は、有効な実際のジェネリックパラメーターがクラスから継承するクラスになることを示していますCOMPARABLE一般的な制約により、SORTED_LIST缶の要素を実際にソートできるようになります。

クラス
    SORTED_LIST  [ G-  >  COMPARABLE ]

Javaのジェネリックス

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

.NETの汎用性[C#、VB.NET]

ジェネリックは、1999年に開始されたMicrosoft Researchの調査プロトタイプに基づいて、2005年11月に.NET Framework2.0の一部として追加されました。 [23] Javaのジェネリックと同様ですが、.NETジェネリックは型消去を適用しませんが、ジェネリックをreificationを使用したランタイムのファーストクラスメカニズムこの設計上の選択により、ジェネリック型を保持したままリフレクションを許可したり、消去の制限(ジェネリック配列を作成できないなど)を緩和したりするなど、追加の機能が提供されます。[24] [25]これは、ランタイムキャストによるパフォーマンスへの影響がなく、通常は高額であることも意味します。ボクシングの変換プリミティブ型と値型をジェネリック引数として使用すると、それらは特殊な実装を取得し、効率的なジェネリックコレクションとメソッドを可能にします。C ++やJavaと同様に、Dictionary <string、List <int >>などのネストされたジェネリック型は有効な型ですが、コード分析デザインルールのメンバー署名には使用しないことをお勧めします。[26]

.NETでは、ジェネリック型whereを値型、クラス、コンストラクター、およびインターフェイスの実装に制限するなど、キーワードを使用して6種類のジェネリック型制約を使用できます。[27]以下は、インターフェース制約のある例です。

 システムを使用する;

クラス サンプル
{{
    static  void  Main ()
    {{
        int [  ] array  =  {  0、1、2、3 } ; _  _ _  _  
        MakeAtLeast < int >(array  2 );  //配列を{2、2、2、3}に変更します
        foreach  配列内のint  i   
            コンソールWriteLine i );  //結果を出力します。
        コンソールReadKey true );
    }

    static  void  MakeAtLeast < T >(T []  list  T  lower  ここで、 T   IComparable < T >
    {{
        for  int  i  =  0 ;  i  < リスト長さ;  i ++)
            if  list [ i ] 。CompareTo 最低 <  0 
                リスト[ i ]  = 最低;
    }
}

このMakeAtLeast()メソッドでは、ジェネリック型の要素を使用して配列を操作できますTメソッドの型制約は、このメソッドがTジェネリックIComparable<T>インターフェイスを実装するすべての型に適用できることを示しています。これにより、型が比較をサポートしていない場合にメソッドが呼び出された場合に、コンパイル時エラーが発生します。インターフェイスはジェネリックメソッドを提供しますCompareTo(T)

上記のメソッドは、ジェネリック型を使用せずに、非ジェネリックArray型を使用して作成することもできます。ただし、配列は反変であるため、キャストは型セーフではなく、コンパイラーは、ジェネリック型を使用するときにキャッチされる可能性のある特定のエラーを見つけることができません。さらに、このメソッドはobject代わりにsとして配列アイテムにアクセスする必要があり、 2つの要素を比較するためにキャストする必要があります。(このような型のような値型の場合、ボクシング変換intが必要ですが、これは、標準のコレクションクラスで行われるように、クラスを使用して回避できます。) Comparer<T>

ジェネリック.NETクラスの静的メンバーの注目すべき動作は、実行時型ごとの静的メンバーのインスタンス化です(以下の例を参照)。

    //ジェネリッククラス
    publicclass GenTest  < T > { //静的変数-リフレクションでタイプごとに作成されますstaticCountedInstances OnePerType = new CountedInstances (); 
    
        
             

        //データ
        メンバーprivateT  mT  ;

        //単純なコンストラク
        ターpublicGenTest  T pT { mT = pT ; } } 
        
              
        
    

    //
    クラスpublicclass  CountedInstances { //静的変数-これはインスタンスごとに1回インクリメントされますpublicstatic int Counter ; 
    
        
           

        //単純なコンストラクター
        publicCountedInstances  { //オブジェクトのインスタンス化中にカウンターを1つ増やしますCountedInstances カウンター++; } }
        
            
            
        
    

  //メインコードのエントリポイント
  //実行の終了時に、CountedInstances.Counter = 2 
  GenTest < int >  g1  =  new  GenTest < int >(1 ); 
  GenTest < int >  g11  =  new  GenTest < int >(11 ); 
  GenTest < int >  g111  =  new  GenTest < int >(111 ); 
  GenTest < double >  g2  = 新しい GenTest< double >(1.0 );

Delphiの一般性

DelphiのObjectPascal方言は、Delphi 2007リリースでジェネリックスを取得しました。最初は(現在は廃止されている).NETコンパイラでのみ使用され、その後Delphi2009リリースでネイティブコードに追加されました。Delphiジェネリックのセマンティクスと機能は、主に.NET 2.0のジェネリックが持っているものをモデルにしていますが、実装は必然的にまったく異なります。上記の最初のC#の例を多かれ少なかれ直接翻訳します。

プログラム サンプル;

{$ APPTYPE CONSOLE}


  Genericsを使用します。デフォルト;  // IComparer <>の場合

タイプ
  TUtils  = クラス
    クラス プロシージャ MakeAtLeast < T > Arr  TArray < T >;  const  Lower T ; Comparer IComparer < T > ; 過負荷; クラスプロシージャMakeAtLeast < T > Arr TArray < T >; const Lower T ; 過負荷 
        
           ; 
  終了;

クラス プロシージャ TUtils MakeAtLeast < T > Arr  TArray < T >;  const  Lower T ; Comparer IComparer < T > ; var I 整数; Comparer = nilの場合に開始しComparer := TComparer < T >。デフォルト; 私のため:=到着 
   

   

         
      to  High Arr  do 
    ifComparer  _ 比較Arr [ I ] Lowest < 0 then Arr [ I ] := Lower ; 終了;    
        


クラス プロシージャ TUtils MakeAtLeast < T > Arr  TArray < T >;  const  Lower  T ; 
MakeAtLeast 
  < T > Arr Lowest nil ;を開始します。終了;  


var 
  Ints  TArray <整数>; 
   整数; 
Ints := TArray <整数>を開始します。
  作成0、1、2、3 ; _ _ _ _ _ _ TUtils MakeAtLeast <整数> Ints 2 ; Ints do WriteLn Value 値の場合; ReadLn ; 終わり     
   
      
    
  

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

FreePascalの一般性

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

DelphiとFreePascalの例:

// Delphiスタイルの
ユニット A ;

{$ ifdef fpc} 
  {$ mode delphi} 
{$ endif}

インターフェース

タイプ
  TGenericClass < T >  = クラス
    関数 Foo const  AValue  T  T ; 
  終了;

実装

関数 TGenericClass < T >。Foo const  AValue  T  T ; 
結果の開始
  := AValue + AValue ; 終了;    


終了

// PascalのObjFPCスタイル
ユニット Bを解放します;

{$ ifdef fpc} 
  {$ mode objfpc} 
{$ endif}

インターフェース

タイプ
  ジェネリック TGenericClass < T >  = クラス
    関数 Foo const  AValue  T  T ; 
  終了;

実装

関数 TGenericClass Foo const  AValue  T  T ; 
結果の開始
  := AValue + AValue ; 終了;    


終了

//使用例、Delphiスタイル
プログラム TestGenDelphi ;

{$ ifdef fpc} 
  {$ mode delphi} 
{$ endif}


  A Bを使用します;

var 
  GC1  A TGenericClass <整数>; 
  GC2  B TGenericClass <文字列>; 
GC1を開始します
  = A。TGenericClass <整数>。作成; GC2 = B。TGenericClass <文字列>。作成; WriteLn GC1。Foo 100 ; _ // 200 WriteLn GC2   
    
   
  Foo 'hello' ));  // 
  hellohelloGC1 無料; 
  GC2 無料; 
終了

//使用例、ObjFPCスタイル
プログラム TestGenDelphi ;

{$ ifdef fpc} 
  {$ mode objfpc} 
{$ endif}


  A Bを使用します;

// ObjFPC
タイプ
  で必要TAGenericClassInt  =  specializeA  TGenericClass <整数>; TBGenericClassString =スペシャライズB。TGenericClass <文字列>; var GC1 TAGenericClassInt ; GC2 TBGenericClassString ; GC1を開始します:= TAGenericClassInt 作成; GC2 := TBGenericClassString 作成; WriteLn GC1
     

   
   

    
    
  Foo 100 ));  // 200 
  WriteLn GC2。Foo ' hello ' )); // hellohelloGC1 無料; GC2 無料; 終了 
  
  

関数型言語

Haskellの一般性

Haskell型クラスメカニズムはジェネリックプログラミングをサポートしています。Haskellで事前定義された6つの型クラス(等しいかどうかを比較できる型、および値を文字列としてレンダリングできる型を含む)には、派生インスタンスをサポートするという特別なプロパティがあります。これは、新しい型を定義するプログラマーが、クラスインスタンスを宣言するときに通常必要となるようなクラスメソッドの実装を提供せずに、この型がこれらの特殊な型クラスの1つのインスタンスであると述べることができることを意味します。必要なすべてのメソッドは、型の構造に基づいて「派生」します。つまり、自動的に構築されます。たとえば、次の二分木のタイプの宣言EqShowそれはクラスのインスタンスであると述べておりEqShow

データ BinTreea  =リーフa | _  Node BinTree a a BinTree a deriving Eq Show          
        

これにより、等式関数(==)と文字列表現関数( )が、それ自体がそれらの操作をサポートしている 場合show、任意のタイプのフォームに対して自動的に定義されます。BinTree TT

の派生インスタンスのサポートにより、それらのメソッドEqジェネリックは、パラメトリックポリモーフィック関数とは質的に異なります。これらの「関数」(より正確には、タイプインデックス付きの関数ファミリー)は、さまざまなタイプの値に適用できますがこれらは引数タイプごとに動作が異なり、新しいタイプのサポートを追加するための作業はほとんど必要ありません。Ralf Hinze(2004)は、特定のプログラミング手法によって、ユーザー定義型クラスでも同様の効果が得られることを示しています。他の研究者は、HaskellとHaskellの拡張(以下で説明)のコンテキストで、これと他の種類の一般性へのアプローチを提案しました。 Show==show

ポリープ

PolyPは、 Haskellの最初のジェネリックプログラミング言語拡張機能でしたPolyPでは、ジェネリック関数はポリタイプと呼ばます。この言語は、通常のデータ型のパターンファンクターの構造に対する構造的帰納法を介してそのようなポリタイプ関数を定義できる特別な構造を導入しています。PolyPの通常のデータ型は、Haskellデータ型のサブセットです。通常のデータ型tは種類 *→*である必要があり、aが定義内の正式な型引数である場合、 tへのすべての再帰呼び出しは形式taである必要がありますこれらの制限により、再帰呼び出しの形式が異なるネストされたデータ型だけでなく、より種類の多いデータ型も除外されます。PolyPのフラット化関数を例として示します。

   flatten  :: 通常 d  =>  d a-  > [ a  ] flatten = cata fl 
      

    ポリタイプfl  ::  f  a  [ a ]  ->  [ a ]
     ケース fof g  + h- >いずれか
       flfl g * h- > \ x y -> fl x ++ fl y ()- > \ x -> []パー-> \ x- > [ x ] Rec- > \ x- > x    
               
           
           
           
       d @ g-  >  concat   平らにする  pmap  fl 
       Con  t-  >  \ x-  >  []

    カタ:: 通常 d  =>  FunctorOf d a b- > b -> d a-  > b         
Generic Haskell

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

  • 型インデックス値は、さまざまなHaskell型コンストラクター(ユニット、プリミティブ型、合計、積、およびユーザー定義型コンストラクター)でインデックス付けされた値として定義されます。さらに、コンストラクターのケースを使用して特定のコンストラクターの型インデックス値の動作を指定し、デフォルトのケースを使用してある汎用定義を別の汎用定義で再利用することもできます

結果のタイプインデックス値は、任意のタイプに特化できます。

  • 種類インデックス型は、種類に対してインデックス付けされた型であり、 *k→k 'の両方に大文字と小文字を区別することによって定義されます。インスタンスは、kind-indexed型をkindに適用することによって取得されます。
  • 一般的な定義は、タイプまたは種類に適用することで使用できます。これは汎用アプリケーションと呼ばれます。結果は、適用される一般的な定義の種類に応じて、タイプまたは値になります。
  • ジェネリック抽象化により、(特定の種類の)型パラメーターを抽象化することによってジェネリック定義を定義できます。
  • 型インデックス型は、型コンストラクターを介してインデックス付けされる型です。これらを使用して、より複雑な汎用値に型を与えることができます。結果として得られるタイプインデックス付きタイプは、任意のタイプに特化できます。

例として、Generic Haskellの等式関数:[28]

   タイプ Eq  {[  *  ]}  t1  t2  =  t1-  >  t2-  > ブール
   タイプ Eq  {[  k-  >  l  ] }  t1  t2  =  forall  u1u2  Eq {[ k ]} u1 u2- > Eq {[ l ]} t1 u1 t2 u2                

   eq  { |  t  ::  k  | }  ::  Eq  {[  k  ]}  t  t 
   eq  { |  ユニット | }  _  _  =  True 
   eq  { |  :+: | }  eqA  eqB  Inl  a1  Inl  a2  =  eqA  a1  a2 
   eq  { |  :+: | }  eqA  eqB  Inr  b1  Inr  b2  =  eqB  b1  b2 
   eq  { |  :+: | }  eqA  eqB  _  _  =  False 
   eq  { |  :*: | }  eqA  eqB  a1  :*: b1  a2  :*: b2  =  eqA  a1  a2  &&  eqB  b1  b2 
   eq  { |  Int  | }  =  == 
   eq  { | チャー | }  =  == 
   eq  { |  ブール | }  =  == 

クリーン

Cleanは、GHC> = 6.0でサポートされているように、ジェネリックプログラミングベースのPolyPとジェネリックHaskellを提供します。それはそれらのように種類によってパラメータ化されますが、オーバーロードを提供します。

他の言語

MLファミリーの言語は、パラメトリックポリモーフィズムとファンクターと呼ばれるジェネリックモジュールを介したジェネリックプログラミングをサポートしています。Standard MLOCamlはどちらも、クラステンプレートやAdaのジェネリックパッケージに似た機能を提供します。スキームの構文の抽象化は、一般性にも関係しています。これらは、実際にはC ++テンプレートのスーパーセットです。

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

Adaから派生したVHDLにも、汎用機能があります。[30]

Cは、次のキーワードを使用して「型ジェネリック式」をサポートします。 [31]_Generic

#define cbrt(x)_Generic((x)、long double:cbrtl、\ 
                              default:cbrt、\ 
                              float:cbrtf)(x)

も参照してください

参考文献

  1. ^ Lee、Kent D.(2008年12月15日)。プログラミング言語:アクティブラーニングアプローチシュプリンガーサイエンス&ビジネスメディア。pp。9–10。ISBN  978-0-387-79422-8
  2. ^ ミルナー、R。; モリス、L。; Newey、M。(1975)。「反射型とポリモーフィック型の計算可能関数の論理」。プログラムの証明と改善に関する会議の議事録
  3. ^ ガンマ、エーリヒ; ヘルム、リチャード; ジョンソン、ラルフ; ジョン・ブリシディーズ(1994)。デザインパターンアディソン-ウェスリー。ISBN  0-201-63361-2
  4. ^ Musser&Stepanov1989
  5. ^ Musser、David R。; ステパノフ、アレクサンダーA.ジェネリックプログラミング(PDF)
  6. ^ アレクサンダーステパノフ; ポール・マッジョネス(2009年6月19日)。プログラミングの要素アディソン-ウェスリープロフェッショナル。ISBN 978-0-321-63537-2
  7. ^ Musser、David R。; ステパノフ、アレクサンダーA.(1987)。「Adaの遺伝的アルゴリズムのライブラリ」。1987年のAdamに関する年次ACMSIGAda国際会議の議事録:216–225。CiteSeerX10.1.1.588.7431_ 土井10.1145 /317500.317529ISBN  0897912438S2CID795406 _
  8. ^ AlexanderStepanovとMengLee:標準テンプレートライブラリ。HP Laboratoriesテクニカルレポート95-11(R.1)、1995年11月14日
  9. ^ Matthew H. Austern:一般的なプログラミングとSTL:C ++標準テンプレートライブラリの使用と拡張。Addison-Wesley Longman Publishing Co.、Inc。米国マサチューセッツ州ボストン1998
  10. ^ Jeremy G. Siek、Lie-Quan Lee、Andrew Lumsdaine:Boost Graph Library:ユーザーガイドおよびリファレンスマニュアル。アディソン-ウェスリー2001
  11. ^ ステパノフ、アレクサンダー。STLの短い歴史(PDF)
  12. ^ a b Stroustrup、Bjarne。現実の世界で、そして現実の世界のために言語を進化させる:C ++ 1991-2006(PDF)土井10.1145 /1238844.1238848S2CID7518369_  
  13. ^ Lo Russo、Graziano。「A.ステパノフへのインタビュー」
  14. ^ ローランドバックハウス; パトリック・ヤンソン; Johan Jeuring; Lambert Meertens(1999)。ジェネリックプログラミング–はじめに(PDF)
  15. ^ Lämmel、ラルフ; ペイトンジョーンズ、サイモン。「ボイラープレートを廃棄する:ジェネリックプログラミングの実用的なデザインパターン」(PDF)Microsoft 2016年10月16日取得
  16. ^ ガブリエルドスレイス; JaakkoJärvi(2005)。「ジェネリックプログラミングとは?(プレプリントLCSD'05)」(PDF)2005年12月25日にオリジナル(PDF)からアーカイブされました。
  17. ^ R。ガルシア; J.Järvi; A.ラムズデイン; J. Siek; J.ウィルコック(2005)。「ジェネリックプログラミング(プレプリント)の言語サポートの拡張比較研究」。CiteSeerX10.1.1.110.122_  {{cite journal}}引用ジャーナルには|journal=ヘルプ)が必要です
  18. ^ Stroustrup、Dos Reis(2003):概念-テンプレート引数チェックの設計上の選択
  19. ^ Stroustrup、Bjarne(1994)。「15.5コード複製の回避」。C ++の設計と進化マサチューセッツ州レディング:アディソン-ウェスリー。pp。346–348。Bibcode1994dec..book ..... S。ISBN 978-81-317-1608-3
  20. ^ 明るい、ウォルター。「Dのヴォルデモートタイプ」ドブス博士2015年6月3日取得
  21. ^ オブジェクト指向ソフトウェア構築、 Prentice Hall、1988、およびオブジェクト指向ソフトウェア構築、第2版、 Prentice Hall、1997。
  22. ^ Eiffel:The Language、 Prentice Hall、1991年。
  23. ^ .NET / C#ジェネリックスの歴史:1999年2月の写真
  24. ^ C#:昨日、今日、そして明日:アンダース・ヘルスバーグへのインタビュー
  25. ^ C#、Java、およびC ++のジェネリック
  26. ^ コード分析CA1006:メンバー署名にジェネリック型をネストしないでください
  27. ^ 型パラメーターの制約(C#プログラミングガイド)
  28. ^ GenericHaskellユーザーガイド
  29. ^ 例によるVerilog、参照用の残りのセクション。ブレインC.リードラー、フルアークプレス、2011。ISBN978-0-9834973-0-1 
  30. ^ https://www.ics.uci.edu/~jmoorkan/vhdlref/generics.htmlVHDLリファレンス
  31. ^ WG14 N1516委員会ドラフト— 2010年10月4日

ソース

さらに読む

外部リンク

C ++ / D
C#/。NET
Delphi / Object Pascal
エッフェル
Haskell
Java