--C++ Primer笔记----第十六章 模板和泛型编程------------------------------------------------------------
Chapter 16. Templates and Generic Programming
第十六章 模板和泛型编程
--------------------------------------------------------------------------------
Generic programming involves writing code in a way that is independent of any particular type. When we use a generic program we supply the type(s) or value(s) on which that instance of the program will operate.
所谓泛型编程就是以独立于任何特定类型的方式编写代码。使用泛型程序时,我们需要提供具体程序实例所操作的类型或值。
--------------------------------------------------------------------------------
Templates are the foundation of generic programming. We can, and have, used templates without understanding how they are defined.
模板是泛型编程的基础。
----------------------------------------------------------------
Generic programming, like object-oriented programming, relies on a form of polymorphism. The polymorphism in OOP applies at run time to classes related by inheritance. We can write code that uses such classes in ways that ignore the type differences among the base and derived classes. As long as we use references or pointers to the base type, we can use the same code on objects of the base type or a type derived from that type.
泛型编程与面向对象编程一样,都依赖于某种形式的多态性。面向对象编程中的多态性在运行时应用于存在继承关系的类。我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异。只要使用基类的引用或指针,基类类型或派生类类型的对象就可以使用相同的代码。
Generic programming lets us write classes and functions that are polymorphic across unrelated types at compile time. A single class or function can be used to manipulate objects of a variety of types. The standard library containers, iterators, and algorithms are good examples of generic programming. The library defines each of the containers, iterators, and algorithms in a type-independent manner. We can use library classes and functions on most any kind of type.
在泛型编程中,我们所编写的类和函数能够多态地用于跨越编译时不相关的类型。一个类或一个函数可以用来操纵多种类型的对象。标准库中的容器、迭代器和算法是很好的泛型编程的例子。标准库用独立于类型的方式定义每个容器、迭代器和算法,因此几乎可以在任意类型上使用标准库的类和函数。
----------------------------------------------------------------
- 两个原始函数:
-
View Code
// returns 0 if the values are equal, -1 if v1 is smaller, 1 if v2 is smaller int compare(const string &v1, const string &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } int compare(const double &v1, const double &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; }
-
- 相应模板函数:
-
View Code
// implement strcmp-like generic compare function // returns 0 if the values are equal, 1 if v1 is larger, -1 if v1 is smaller template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; }
-
题外(关于类模板) <Template Parameters 模板形参>:
As with a function parameter, the name chosen by the programmer for a template parameter has no intrinsic meaning. In our example, we named compare's template type parameter T, but we could have named it anything:
像函数形参一样,程序员为模板形参选择的名字没有本质含义。在我们的例子中,将 compare 的模板类型形参命名为 T,但也可以将它命名为任意名字:
// equivalent template definition template <class Glorp> int compare(const Glorp &v1, const Glorp &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; }
This code defines the same compare template as before.
该代码定义的 compare 模板与前面一样。
The only meaning we can ascribe to a template parameter is to distinguish whether the parameter is a type parameter or a nontype parameter. If it is a type parameter, then we know that the parameter represents an as yet unknown type. If it is a nontype parameter, we know it is an as yet unknown value.
可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类型形参。如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。
When we wish to use the type or value that a template parameter represents, we use the same name as the corresponding template parameter. For example, all references to Glorp in the compare function template will be resolved to the same type when the function is instantiated.
如果希望使用模板形参所表示的类型或值,可以使用与对应模板形参相同的名字。例如,compare 函数中所有的 Glorp 引用将在该函数被实例化时确定为同一类型。
----------------------------------------------------------------
Analogously, template parameters represent types or values we can use in the definition of a class or function. For example, our compare function declares one type parameter named T. Inside compare, we can use the name T to refer to a type. Which actual type T represents is determined by the compiler based on how the function is used.
同样,模板形参表示可以在类或函数的定义中使用的类型或值。例如,compare 函数声明一个名为 T 的类型形参。在 compare 内部,可以使用名字 T 引用一个类型,T 表示哪个实际类型由编译器根据所用的函数而确定。
----------------------------------------------------------------
--------------------------------------------------------------------------------
Using a Function Template
使用函数模板
When we use a function template, the compiler infers what template argument(s) to bind to the template parameter(s). Once the compiler determines the actual template argument(s), it instantiates an instance of the function template for us. Essentially, the compiler figures out what type to use in place of each type parameter and what value to use in place of each nontype parameter. Having deduced the actual template arguments, it generates and compiles a version of the function using those arguments in place of the corresponding template parameters. The compiler takes on the tedium of (re)writing the function for each type we use.
使用函数模板时,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。编译器承担了为我们使用的每种类型而编写函数的单调工作。
int main () { // T is int; // compiler instantiates int compare(const int&, const int&) cout << compare(1, 0) << endl; // T is string; // compiler instantiates int compare(const string&, const string&) string s1 = "hi", s2 = "world"; cout << compare(s1, s2) << endl; return 0; }
the compiler will instantiate two different versions of compare. The compiler will create one version that replaces T by int and a second version that uses string in place of T.
编译器将实例化 compare 的两个不同版本,编译器将用 int 代替 T 创建第一个版本,并用 string 代替 T 创建第二个版本。
--------------------------------------------------------------------------------
inline Function Templates
inline 函数模板
A function template can be declared inline in the same way as a nontemplate function. The specifier is placed following the template parameter list and before the return type. It is not placed in front of the template keyword.
函数模板可以用与非模板函数一样的方式声明为 inline。说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前。
// ok: inline specifier follows template parameter list template <typename T> inline T min(const T&, const T&); // error: incorrect placement of inline specifier inline template <typename T> T min(const T&, const T&);
================================================================================
Defining a Class Template
定义类模板
--------------------------------------------------------------------------------
// 实现 Queue 类的接口 : template <class Type> class Queue { public: Queue (); // default constructor Type &front (); // return element from head of Queue const Type &front () const; void push (const Type &); // add element to back of Queue void pop(); // remove element from head of Queue bool empty() const; // true if no elements in the Queue private: // ... };
A class template is a template, so it must begin with the keyword template followed by a template parameter list. Our Queue template takes a single template type parameter named Type.
类模板也是模板,因此必须以关键字 template 开头,后接模板形参表。Queue 模板接受一个名为 Type 的模板类型形参。
With the exception of the template parameter list, the definition of a class template looks like any other class. A class template may define data, function, and type members; it may use access labels to control access to those members; it defines constructors and destructors; and so on. In the definition of the class and its members, we can use the template parameters as stand-ins for types or values that will be supplied when the class is used.
除了模板形参表外,类模板的定义看起来与任意其他类问相似。类模板可以定义数据成员、函数成员和类型成员,也可以使用访问标号控制对成员的访问,还可以定义构造函数和析构函数等等。在类和类成员的定义中,可以使用模板形参作为类型或值的占位符,在使用类时再提供那些类型或值。
For example, our Queue template has one template type parameter. We can use that parameter anywhere a type name can be used. In this template definition, we use Type to name the return type from the overloaded front operations and as the parameter type for the push operation.
例如,Queue 模板有一个模板类型形参,可以在任何可以使用类型名字的地方使用该形参。在这个模板定义中,用 Type 指定重载 front 操作的返回类型以及作为 push 操作的形参类型。
--------------------------------------------------------------------------------
Using a Class Template
使用类模板
In contrast to calling a function template, when we use a class template, we must explicitly specify arguments for the template parameters:
与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参:
Queue<int> qi; // Queue that holds ints Queue< vector<double> > qc; // Queue that holds vectors of doubles Queue<string> qs; // Queue that holds strings
The compiler uses the arguments to instantiate a type-specific version of the class. Essentially, the compiler rewrites our Queue class replacing Type by the specified actual type provided by the user. In this case, the compiler will instantiate three classes: a version of Queue with Type replaced by int, a second Queue class that uses vector<double> in place of Type, and a third that replaces Type by string.
编译器使用实参来实例化这个类的特定类型版本。实质上,编译器用用户提供的实际特定类型代替 Type,重新编写 Queue 类。在这个例子中,编译器将实例化三个 Queue 类:第一个用 int 代替 Type,第二个用 vector<double> 代替 Type,第三个用 string 代替 Type。
--------------------------------------------------------------------------------