C++/CLI でのジェネリックの概要Overview of Generics in C++/CLI

ジェネリックは、共通言語ランタイムによってサポートされるパラメーター化された型です。Generics are parameterized types supported by the common language runtime. パラメーター化された型とは、ジェネリックを使用するときに指定される、不明な型パラメーターを使用して定義される型です。A parameterized type is a type that is defined with an unknown type parameter that is specified when the generic is used.

なぜジェネリックかWhy Generics?

C++ はテンプレートをサポートします。また、テンプレートとジェネリックのどちらも、型指定されたコレクション クラスを作成するパラメーター化された型をサポートします。C++ supports templates and both templates and generics support parameterized types to create typed collection classes. ただし、テンプレートにはコンパイル時のパラメーター化が用意されています。However, templates provide compile-time parameterization. テンプレートの定義を含むアセンブリを参照して、テンプレートの新しい特殊化を作成することはできません。You cannot reference an assembly containing a template definition and create new specializations of the template. コンパイルが完了すると、特殊化されたテンプレートは他のクラスやメソッドと同じように見えます。Once compiled, a specialized template looks like any other class or method. 対照的に、ジェネリックはパラメーター化された型として MSIL に出力され、ランタイムではパラメーター化された型と見なされます。つまり、ジェネリック型を含むアセンブリを参照するソース コードでは、ジェネリック型の特殊化を作成できます。In contrast, generics are emitted in MSIL as a parameterized type known by the runtime to be a parameterized type; source code that references an assembly containing a generic type can create specializations of the generic type. 標準 C++ テンプレートとジェネリックの比較の詳細については、「Generics and Templates (C++/CLI) (ジェネリックとテンプレート (C++ /CLI))」を参照してください。For more information on the comparison of standard C++ templates and generics, see Generics and Templates (C++/CLI).

ジェネリック関数と型Generic Functions and Types

クラスの型は、マネージド型である限り、ジェネリック型にすることができます。Class types, as long as they are managed types, may be generic. この一例として List クラスがあります。An example of this might be a List class. リスト内のオブジェクトの型は、型パラメーターです。The type of an object in the list would be the type parameter. さまざまな型のオブジェクトに List クラスが必要な場合、ジェネリックの前には、項目の型として System::Object を受け取る List が使用されていたことがあります。If you needed a List class for many different types of objects, before generics you might have used a List that takes System::Object as the item type. ただし、この場合、任意のオブジェクト (不適切な型のオブジェクトを含む) をリストに使用できます。But that would allow any object (including objects of the wrong type) to be used in the list. そのようなリストは、型指定のないコレクション クラスと呼ばれます。Such a list would be called an untyped collection class. できることは、実行時に型をチェックし、例外をスローすることくらいです。At best, you could check the type at runtime and throw an exception. また、テンプレートをする場合もありました。この場合、テンプレートをアセンブリにコンパイルすると、そのジェネリックの質が失われます。Or, you might have used a template, which would lose its generic quality once compiled into an assembly. このアセンブリの利用者は、テンプレートの独自の特殊化を作成できないでしょう。Consumers of your assembly could not create their own specializations of the template. ジェネリックを使用すると、型指定されたコレクション クラスを作成できます。たとえば List<int> ("int のリスト" と読みます) や List<double> ("double のリスト") の場合、受け取るようにコレクションに設計されていない型を、型指定されたコレクションに適用しようとすると、コンパイル時エラーが発生します。Generics allow you to create typed collection classes, say List<int> (read as "List of int") and List<double> ("List of double") which would generate a compile-time error if you tried to put a type that the collection was not designed to accept into the typed collection. さらに、これらの型はコンパイル後もジェネリックのままです。In addition, these types remain generic after they are compiled.

ジェネリック クラスの構文の説明については、「Generic Classes (C++/CLI) (ジェネリック クラス (C++/CLI))」を参照してください。A description of the syntax of generic classes may be found in Generic Classes (C++/CLI). 新しい名前空間 System.Collections.Generic では、Dictionary<TKey,TValue>List<T>LinkedList<T> などの一連のパラメーター化されたコレクション型が導入されました。A new namespace, System.Collections.Generic, introduces a set of parameterized collection types including Dictionary<TKey,TValue>, List<T> and LinkedList<T>.

インスタンスおよび静的クラス メンバー関数、デリゲート、およびグローバル関数は、いずれもジェネリックにすることができます。Both instance and static class member functions, delegates, and global functions may also be generic. ジェネリック関数が必要になるのは、関数のパラメーターが不明な型である場合や、関数自体がジェネリック型を使用する必要がある場合です。Generic functions may be necessary if the function's parameters are of an unknown type, or if the function itself must work with generic types. 多くの場合、過去には不明なオブジェクト型のパラメーターとして System::Object が使用されていましたが、代わりにジェネリック型パラメーターを使用すると、よりタイプ セーフなコードにすることができます。In many cases where System::Object may have been used in the past as a parameter for an unknown object type, a generic type parameter may be used instead, allowing for more type-safe code. 関数に設計されていない型を渡そうとすると、コンパイル時にエラーというフラグが立てられます。Any attempt to pass in a type that the function was not designed for would be flagged as an error at compile time. 関数パラメーターとして System::Object を使用すると、本来は関数で処理できないオブジェクトを誤って渡した場合に検出されないので、不明なオブジェクト型を関数本体の特定の型にキャストし、InvalidCastException の可能性を考慮する必要があります。Using System::Object as a function parameter, the inadvertent passing of an object that the function wasn't intended to deal with would not be detected, and you would have to cast the unknown object type to a specific type in the function body, and account for the possibility of an InvalidCastException. ジェネリックを使用する場合、コードで関数にオブジェクトを渡そうとすると型の競合が発生するので、関数本体を確実に正しい型にすることができます。With a generic, code attempting to pass an object to the function would cause a type conflict so the function body is guaranteed to have the correct type.

同じ利点が、ジェネリックに基づいて構築されたコレクション クラスにも当てはまります。The same benefits apply to collection classes built on generics. これまでのコレクション クラスでは、要素をコレクションに格納するために System::Object が使用されていました。Collection classes in the past would use System::Object to store elements in a collection. コレクションが設計されていない型のオブジェクトを挿入しても、コンパイル時にはフラグが立てられませんでした。オブジェクトが挿入されたときであっても、多くの場合は同様です。Insertion of objects of a type that the collection was not designed for was not flagged at compile time, and often not even when the objects were inserted. 通常、オブジェクトはコレクション内でアクセスされるときに他の型にキャストされます。Usually, an object would be cast to some other type when it was accessed in the collection. キャストが失敗したときにのみ、予期しない型が検出されます。Only when the cast failed would the unexpected type be detected. ジェネリックでは、コンパイル時のこの問題を解決するために、ジェネリック コレクションの型パラメーターと一致しない (または暗黙的に変換される) 型を挿入するコードを検出しています。Generics solves this problem at compile time by detecting any code that inserts a type that doesn't match (or implicitly convert to) the type parameter of the generic collection.

構文の説明については、「Generic Functions (C++/CLI) (ジェネリック関数 (C++/CLI))」を参照してください。For a description of the syntax, see Generic Functions (C++/CLI).

ジェネリックで使用される用語Terminology Used With Generics

型パラメーターType Parameters

ジェネリック宣言には、"型パラメーター" と呼ばれる 1 つ以上の不明な型が含まれています。A generic declaration contains one or more unknown types known as type parameters. 型パラメーターには、ジェネリック宣言の本体内で型を表す名前が付けられます。Type parameters are given a name which stands for the type within the body of the generic declaration. 型パラメーターは、ジェネリック宣言の本体内で型として使用されます。The type parameter is used as a type within the body of the generic declaration. List<T> のジェネリック宣言には型パラメーター T が含まれています。The generic declaration for List<T> contains the type parameter T.

型引数Type Arguments

"型引数" は、ジェネリックが 1 つまたは複数の特定の型に特殊化される場合に、型パラメーターの代わりに使用される実際の型です。The type argument is the actual type used in place of the type parameter when the generic is specialized for a specific type or types. たとえば、intList<int> の型引数です。For example, int is the type argument in List<int>. ジェネリック型引数として使用できるのは、値の型とハンドルの型のみです。Value types and handle types are the only types allowed in as a generic type argument.

構築型Constructed Type

ジェネリック型から構築された型は、"構築型" と呼ばれます。A type constructed from a generic type is referred to as a constructed type. List<T> のように完全に指定されていない型は、"オープン構築型" です。List<double>, のように完全に指定された型は、"クローズ構築型" または "特殊化された型" です。A type not fully specified, such as List<T> is an open constructed type; a type fully specified, such as List<double>, is a closed constructed type or specialized type. オープン構築型は、他のジェネリック型またはメソッドの定義に使用できます。また、それを囲むジェネリックが指定されるまで完全に指定することはできません。Open constructed types may be used in the definition of other generic types or methods and may not be fully specified until the enclosing generic is itself specified. ジェネリックの基底クラスとしてオープン構築型を使用する例を次に示します。For example, the following is a use of an open constructed type as a base class for a generic:

// generics_overview.cpp
// compile with: /clr /c
generic <typename T>

ref class List {};

generic <typename T>

ref class Queue : public List<T> {};

制約Constraint

制約は、型パラメーターとして使用できる型に対する制限です。A constraint is a restriction on the types that may be used as a type parameter. たとえば、あるジェネリック クラスが、指定したクラスから継承されたクラスのみを受け取ることや、指定したインターフェイスを実装することができます。For example, a given generic class could accept only classes that inherit from a specified class, or implement a specified interface. 詳細については、「Constraints on Generic Type Parameters (C++/CLI) (ジェネリック型パラメーターの (C++/CLI))」を参照してください。For more information, see Constraints on Generic Type Parameters (C++/CLI).

参照型と値の型Reference Types and Value Types

ハンドルの型と値の型は型引数として使用できます。Handles types and value types may be used as type arguments. どちらの型も使用できるジェネリック定義では、参照型の構文になります。In the generic definition, in which either type may be used, the syntax is that of reference types. たとえば、-> 演算子は、最終的に使用される型が参照型か値の型かにかかわらず、型パラメーターの型のメンバーにアクセスするために使用されます。For example, the -> operator is used to access members of the type of the type parameter whether or not the type eventually used is a reference type or a value type. 型引数として値の型を使用すると、値の型はボックス化されず、値の型を直接使用するコードがランタイムによって生成されます。When a value type is used as the type argument, the runtime generates code that uses the value types directly without boxing the value types.

ジェネリック型引数として参照型を使用するときは、ハンドル構文を使用します。When using a reference type as a generic type argument, use the handle syntax. ジェネリック型引数として値の型を使用するときは、型の名前を直接使用します。When using a value type as a generic type argument, use the name of the type directly.

// generics_overview_2.cpp
// compile with: /clr
generic <typename T>

ref class GenericType {};
ref class ReferenceType {};

value struct ValueType {};

int main() {
    GenericType<ReferenceType^> x;
    GenericType<ValueType> y;
}

型パラメーターType Parameters

ジェネリック クラスの型パラメーターは、他の識別子と同じように扱われます。Type parameters in a generic class are treated like other identifiers. ただし、型が不明なので、使用に制限があります。However, because the type is not known, there are restrictions on their use. たとえば、型パラメーター クラスのメンバーとメソッドは、型パラメーターがそれらのメンバーをサポートすることがわかっていない場合に使用できません。For example, you cannot use members and methods of the type parameter class unless the type parameter is known to support these members. つまり、型パラメーターを介してメンバーにアクセスするには、メンバーを含む型を型パラメーターの制約リストに追加する必要があります。That is, to access a member through the type parameter, you must add the type that contains the member to the type parameter's constraint list.

// generics_overview_3.cpp
// compile with: /clr
interface class I {
   void f1();
   void f2();
};

ref struct R : public I {
   virtual void f1() {}
   virtual void f2() {}
   virtual void f3() {}
};

generic <typename T>
where T : I
void f(T t) {
   t->f1();
   t->f2();
   safe_cast<R^>(t)->f3();
}

int main() {
   f(gcnew R());
}

これらの制限は演算子にも適用されます。These restrictions apply to operators as well. 制約のないジェネリック型パラメーターでは、型が == および != 演算子をサポートしていない場合、型パラメーターの 2 つのインスタンスを比較するときにそれらの演算子を使用できません。An unconstrained generic type parameter may not use the == and != operators to compare two instances of the type parameter, in case the type does not support these operators. ジェネリックにはこれらのチェックが必要ですが、テンプレートには必要ありません。なぜなら、ジェネリックは、実行時に制約を満たす任意のクラスで特殊化される場合があり、そのときには無効なメンバーの使用をチェックするには遅すぎるからです。These checks are necessary for generics, but not for templates, because generics may be specialized at runtime with any class that satisfies the constraints, when it is too late to check for the use of invalid members.

型パラメーターの既定のインスタンスは、() 演算子を使用して作成できます。A default instance of the type parameter may be created by using the () operator. 次に例を示します。For example:

T t = T();

ここで、T はジェネリック クラスまたはメソッド定義内の型パラメーターであり、変数がその既定値に初期化されます。where T is a type parameter in a generic class or method definition, initializes the variable to its default value. T が参照クラスの場合は null ポインターになります。T が値クラスの場合、オブジェクトは 0 に初期化されます。If T is a ref class it will be a null pointer; if T is a value class, the object is initialized to zero. これは "既定の初期化子" と呼ばれます。This is called a default initializer.

関連項目See also

ジェネリックGenerics