模板 (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;

上面的代码介绍了具有单个类型参数的泛型函数的模板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. 可以命名为您类似,但通过约定单一大写字母最常用于名称的类型参数。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);

但是,由于这是函数模板和编译器可以推断出的类型T从参数并b,可以就像普通函数一样调用它: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之前添加的常量和引用限定符用在函数调用参数中,未以任何方式进行限定。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{};

关键字等效于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{};

省略号运算符 (...) 可用于定义采用任意数量的零个或多个类型参数的模板: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 标准库中存储整数、 双精度型值、 字符串、 MyClass,const MyClass *,MyClass (& a)。For example, you can use std::vector in the Standard Library to store ints, doubles, strings, MyClass, const MyClass*, MyClass&. 使用模板时的主要限制是类型参数必须支持应用于类型参数的任何操作。The primary restriction when using templates is that a type argument must support any operations that are applied to the type parameters. 例如,如果我们调用最小使用 MyClass,如此示例所示:For example, if we call minimum using MyClass as in this example:

class MyClass
    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));

   // or more realistically:
   vector<shared_ptr<MyClass>> vec2;

矢量和其他标准库容器施加的元素的基本要求T在于T为副本分配和复制构造。The basic requirements that vector and other standard library containers impose on elements of T is that T be copy-assignable and copy-constructible.

非类型参数Non-type parameters

与不同的是 C# 和 Java 等其他语言中的泛型类型,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];
    MyArray() { ... }

注意,模板声明中的语法。Note the syntax in the template declaration. Size_t 值以传入的模板自变量在编译时,并且必须是常量或 constexpr 表达式。The size_t value is passed in as a template argument at compile time and must be constant 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.

作为模板参数的模板Templates as template parameters

模板可以是模板参数。A template can be a template parameter. 在此示例中,MyClass2 具有两个模板参数: typename 参数T和模板参数Arr: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. 事实上,它是错误来指代Arr的类型名称或类参数名称与主体内的MyClass2In 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:: allocator 类是可接受的因此使用一个向量,像这样: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.