テンプレート (C++)Templates (C++)

テンプレートは、のC++一般的なプログラミングの基礎となります。Templates are the basis for generic programming in C++. 厳密に型指定されたC++言語として、では、プログラマが明示的に宣言するか、コンパイラによって推測される、すべての変数に特定の型を持たせる必要があります。As a strongly-typed language, C++ requires all variables to have a specific type, either explicitly declared by the programmer or deduced by the compiler. ただし、操作する型に関係なく、多くのデータ構造とアルゴリズムは同じように見えます。However, many data structures and algorithms look the same no matter what type they are operating on. テンプレートを使用すると、クラスまたは関数の操作を定義し、その操作がどの具象型で動作するかをユーザーが指定できるようになります。Templates enable you to define the operations of a class or function, and let the user specify what concrete types those operations should work on.

テンプレートの定義と使用Defining and using templates

テンプレートは、ユーザーがテンプレートパラメーターに指定した引数に基づいて、コンパイル時に通常の型または関数を生成するコンストラクトです。A template is a construct that generates an ordinary type or function at compile time based on arguments the user supplies for the template parameters. たとえば、次のような関数テンプレートを定義できます。For example, you can define a function template like this:

template <typename T>
T minimum(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

上記のコードでは、1つの型パラメーター Tを持つジェネリック関数のテンプレートについて説明します。このテンプレートの戻り値と呼び出しパラメーター (lhs と rhs) はすべてこの型です。The above code describes a template for a generic function with a single type parameter T, whose return value and call parameters (lhs and rhs) are all of this type. 型パラメーターには任意の名前を指定できますが、慣例により、1つの大文字が最もよく使用されます。You can name a type parameter anything you like, but by convention single upper case letters are most commonly used. Tはテンプレートパラメーターです。typenameキーワードは、このパラメーターが型のプレースホルダーであることを示します。T is a template parameter; the typename keyword says that this parameter is a placeholder for a type. 関数が呼び出されると、コンパイラは、T のすべてのインスタンスを、ユーザーによって指定されるか、コンパイラによって推測される具象型引数に置き換えます。When the function is called, the compiler will replace every instance of T with the concrete type argument that is either specified by the user or deduced by the compiler. コンパイラがテンプレートからクラスまたは関数を生成するプロセスを、テンプレートのインスタンス化と呼びます。minimum<int> は、テンプレート minimum<T>のインスタンス化です。The process in which the compiler generates a class or function from a template is referred to as template instantiation; minimum<int> is an instantiation of the template minimum<T>.

他の場所では、ユーザーは、int に特化したテンプレートのインスタンスを宣言できます。 get_a () と get_b () が int を返す関数であることを前提としています。Elsewhere, a user can declare an instance of the template that is specialized for int. Assume that get_a() and get_b() are functions that return an int:

int a = get_a();
int b = get_b();
int i = minimum<int>(a, b);

ただし、これは関数テンプレートであるため、コンパイラは引数abから T の型を推測できます。通常の関数と同じように呼び出すことができます。However, because this is a function template and the compiler can deduce the type of T from the arguments a and b, you can call it just like an ordinary function:

int i = minimum(a, b);

コンパイラは、最後のステートメントを検出すると、テンプレート内のTのすべての出現箇所をintに置き換える新しい関数を生成します。When the compiler encounters that last statement, it generates a new function in which every occurrence of T in the template is replaced with int:

int minimum(const int& lhs, const int& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

コンパイラが関数テンプレートで型推論を実行する方法に関する規則は、通常の関数の規則に基づいています。The rules for how the compiler performs type deduction in function templates are based on the rules for ordinary functions. 詳細については、「関数テンプレート呼び出しのオーバーロードの解決」を参照してください。For more information, see Overload Resolution of Function Template Calls.

型パラメーターType parameters

上の minimum テンプレートでは、型パラメーター tは、const および参照修飾子が追加される関数呼び出しパラメーターで使用されるまで、どのような方法でも修飾されていないことに注意してください。In the minimum template above, note that the type parameter T is not qualified in any way until it is used in the function call parameters, where the const and reference qualifiers are added.

型パラメーターの数に実際の制限はありません。There is no practical limit to the number of type parameters. 複数のパラメーターはコンマで区切ります。Separate multiple parameters by commas:

template <typename T, typename U, typename V> class Foo{};

キーワードclassと等価typenameこのコンテキストでします。The keyword class is equivalent to typename in this context. 前の例を次のように表すことができます。You can express the previous example as:

template <class T, class U, class V> class Foo{};

省略記号演算子 (...) を使用すると、任意の数の0個以上の型パラメーターを受け取るテンプレートを定義できます。You can use the ellipses operator (...) to define a template that takes an arbitrary number of zero or more type parameters:

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;

任意の組み込みまたはユーザー定義型を型引数として使用できます。Any built-in or user-defined type can be used as a type argument. たとえば、標準ライブラリのstd:: vectorを使用して、 intdoublestd:: stringMyClassconst MyClass*、MyClass&などの型の変数を格納できます。For example, you can use std::vector in the Standard Library to store variables of type int, double, std::string, MyClass, const MyClass*, MyClass&, and so on. テンプレートを使用する場合の主な制限は、型引数が型パラメーターに適用されるすべての操作をサポートする必要があることです。The primary restriction when using templates is that a type argument must support any operations that are applied to the type parameters. たとえば、次の例のように MyClass を使用して minimum を呼び出したとします。For example, if we call minimum using MyClass as in this example:

class MyClass
{
public:
    int num;
    std::wstring description;
};

int main()
{
    MyClass mc1 {1, L"hello"};
    MyClass mc2 {2, L"goodbye"};
    auto result = minimum(mc1, mc2); // Error! C2678
}

MyClass< 演算子のオーバーロードが指定されていないため、コンパイラエラーが生成されます。A compiler error will be generated because MyClass does not provide an overload for the < operator.

特定のテンプレートの型引数がすべて同じオブジェクト階層に属しているという固有の要件はありませんが、このような制限を適用するテンプレートを定義することはできます。There is no inherent requirement that the type arguments for any particular template all belong to the same object hierarchy, although you can define a template that enforces such a restriction. オブジェクト指向の手法とテンプレートを組み合わせることができます。たとえば、派生した * をベクター<Base*> に格納することができます。You can combine object-oriented techniques with templates; for example, you can store a Derived* in a vector<Base*>. 引数はポインターである必要があることに注意してください。Note that the arguments must be pointers

vector<MyClass*> vec;
   MyDerived d(3, L"back again", time(0));
   vec.push_back(&d);

   // or more realistically:
   vector<shared_ptr<MyClass>> vec2;
   vec2.push_back(make_shared<MyDerived>());

std::vector およびその他の標準ライブラリコンテナーが T の要素に適用する基本的な要件は、コピーによる割り当てが可能で、コピーによる構築が可能である T ことです。The basic requirements that std::vector and other standard library containers impose on elements of T is that T be copy-assignable and copy-constructible.

非型パラメーターNon-type parameters

や Java などの他の言語のC#ジェネリック型とC++は異なり、テンプレートでは、値パラメーターとも呼ばれる非型パラメーターがサポートされています。Unlike generic types in other languages such as C# and Java, C++ templates support non-type parameters, also called value parameters. たとえば、標準ライブラリのstd:: arrayクラスに似た次の例のように、配列の長さを指定する定数整数値を指定できます。For example, you can provide a constant integral value to specify the length of an array, as with this example that is similar to the std::array class in the Standard Library:

template<typename T, size_t L>
class MyArray
{
    T arr[L];
public:
    MyArray() { ... }
};

テンプレート宣言の構文に注意してください。Note the syntax in the template declaration. size_t 値はコンパイル時にテンプレート引数として渡され、 constまたはconstexpr式である必要があります。The size_t value is passed in as a template argument at compile time and must be const or a constexpr expression. 次のように使用します。You use it like this:

MyArray<MyClass*, 10> arr;

ポインターや参照を含むその他の種類の値は、非型パラメーターとして渡すことができます。Other kinds of values including pointers and references can be passed in as non-type parameters. たとえば、関数オブジェクトまたは関数オブジェクトへのポインターを渡して、テンプレートコード内の一部の操作をカスタマイズできます。For example, you can pass in a pointer to a function or function object to customize some operation inside the template code.

非型テンプレートパラメーターの型推論Type deduction for non-type template parameters

Visual Studio 2017 以降では、推測で宣言されている非型テンプレート引数の型は、 /std: c++ 17モードでコンパイラによって次のようになります。In Visual Studio 2017 and later, in /std:c++17 mode the compiler deduces the type of a non-type template argument that is declared with auto:

template <auto x> constexpr auto constant = x;

auto v1 = constant<5>;      // v1 == 5, decltype(v1) is int
auto v2 = constant<true>;   // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>;    // v3 == 'a', decltype(v3) is char

テンプレートパラメーターとしてのテンプレートTemplates as template parameters

テンプレートは、テンプレートパラメーターにすることができます。A template can be a template parameter. この例では、MyClass2 には、typename パラメーター Tとテンプレートパラメーター Arrの2つのテンプレートパラメーターがあります。In this example, MyClass2 has two template parameters: a typename parameter T and a template parameter Arr:

template<typename T, template<typename U, int I> class Arr>
class MyClass2
{
    T t; //OK
    Arr<T, 10> a;
    U u; //Error. U not in scope
};

Arrパラメーター自体には本体がないため、パラメーター名は必要ありません。Because the Arr parameter itself has no body, its parameter names are not needed. 実際には、MyClass2の本文内からArrの typename またはクラスパラメーター名を参照するとエラーになります。In fact, it is an error to refer to Arr's typename or class parameter names from within the body of MyClass2. このため、次の例に示すように、 Arrの型パラメーター名を省略できます。For this reason, Arr's type parameter names can be omitted, as shown in this example:

template<typename T, template<typename, int> class Arr>
class MyClass2
{
    T t; //OK
    Arr<T, 10> a;
};

既定のテンプレート引数Default template arguments

クラステンプレートと関数テンプレートには、既定の引数を指定できます。Class and function templates can have default arguments. テンプレートに既定の引数がある場合は、使用時にそのままにしておくことができます。When a template has a default argument you can leave it unspecified when you use it. たとえば、std:: vector テンプレートには、アロケーターの既定の引数があります。For example, the std::vector template has a default argument for the allocator:

template <class T, class Allocator = allocator<T>> class vector;

ほとんどの場合、既定の std:: アロケータークラスが許容されるため、次のようなベクターを使用します。In most cases the default std::allocator class is acceptable, so you use a vector like this:

vector<int> myInts;

ただし、必要に応じて、次のようにカスタムアロケーターを指定することもできます。But if necessary you can specify a custom allocator like this:

vector<int, MyAllocator> ints;

テンプレート引数が複数ある場合、最初の既定の引数の後は、すべての引数に既定の引数が必要です。For multiple template arguments, all arguments after the first default argument must have default arguments.

パラメーターがすべて既定値であるテンプレートを使用する場合は、空の山かっこを使用します。When using a template whose parameters are all defaulted, use empty angle brackets:

template<typename A = int, typename B = double>
class Bar
{
    //...
};
...
int main()
{
    Bar<> bar; // use all default type arguments
}

テンプレートの特殊化Template specialization

場合によっては、テンプレートで任意の型に対してまったく同じコードを定義することはできません。In some cases, it isn’t possible or desirable for a template to define exactly the same code for any type. たとえば、型引数がポインター、std:: wstring、または特定の基底クラスから派生した型である場合にのみ実行されるコードパスを定義できます。For example, you might wish to define a code path to be executed only if the type argument is a pointer, or a std::wstring, or a type derived from a particular base class. そのような場合は、特定の種類のテンプレートの特殊化を定義できます。In such cases you can define a specialization of the template for that particular type. ユーザーがその型を使用してテンプレートをインスタンス化すると、コンパイラは特殊化を使用してクラスを生成し、それ以外のすべての型については、コンパイラがより一般的なテンプレートを選択します。When a user instantiates the template with that type, the compiler uses the specialization to generate the class, and for all other types, the compiler chooses the more general template. すべてのパラメーターが特殊化されている特殊化は完全な特殊化です。Specializations in which all parameters are specialized are complete specializations. 一部のパラメーターのみが特殊化されている場合は、部分的特殊化と呼ばれます。If only some of the parameters are specialized, it is called a partial specialization.

template <typename K, typename V>
class MyMap{/*...*/};

// partial specialization for string keys
template<typename V>
class MyMap<string, V> {/*...*/};
...
MyMap<int, MyClass> classes; // uses original template
MyMap<string, MyClass> classes2; // uses the partial specialization

テンプレートは、特化された各型パラメーターが一意である限り、任意の数の特殊化を持つことができます。A template can have any number of specializations as long as each specialized type parameter is unique. クラステンプレートのみが部分的に特殊化されている場合があります。Only class templates may be partially specialized. テンプレートのすべての完全な特殊化と部分的特殊化は、元のテンプレートと同じ名前空間で宣言する必要があります。All complete and partial specializations of a template must be declared in the same namespace as the original template.

詳細については、「テンプレートの特殊化」を参照してください。For more information, see Template Specialization.