天天看點

C++模闆(c++ primer)

16.1. Template Definitions

16.1. 模闆定義

Let's imagine that we want to write a function to compare two values and indicate whether the first is less than, equal to, or greater than the second. In practice, we'd want to define several such functions, each of which could compare values of a given type. Our first attempt might be to define several overloaded functions:

假設想要編寫一個函數比較兩個值并指出第一個值是小于、等于還是大于第二個值。實踐中,我們可能希望定義幾個這樣的函數,每一個可以比較一種給定類型的值,第一次嘗試可能是定義幾個重載函數:

// 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;
     }
      

These functions are nearly identical: The only difference between them is the type of their parameters. The function body is the same in each function.

這些函數幾乎相同,它們之間唯一的差別是形參的類型,每個函數的函數體是相同的。

Having to repeat the body of the function for each type that we compare is tedious and error-prone. More importantly, we need to knowin advance all the types that we might ever want tocompare. This strategy cannot work if we want to be able to use the function on types that we don't know about.

每個要比較的類型都需要重複函數的函數體,不僅麻煩而且容易出錯。更重要的是,需要事先知道空間可能會比較哪些類型。如果希望将函數用于未知類型,這種政策就不起作用了。

16.1.1. Defining a Function Template

16.1.1. 定義函數模闆

Rather than defining a new function for each type, we can define a singlefunction template. A function template is a type-independent function that is used as a formula for generating a type-specific version of the function. For example, we might write a function template namedcompare, which would tell the compiler how to generate specific versions ofcompare for the types that we want to compare.

我們可以不用為每個類型定義一個新函數,而是隻定義一個函數模闆(function template)。函數模闆是一個獨立于類型的函數,可作為一種方式,産生函數的特定類型版本。例如,可以編寫名為 compare 的函數模闆,它告訴編譯器如何為我們想要比較的類型産生特定的 compare 版本。

The following is a template version of compare:

下面是 compare 的模闆版本:

// 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;
     }
      

A template definition starts with the keyword template followed by atemplate parameter list, which is a comma-separated list of one or moretemplate parameters bracketed by the less-than (<) and greater-than (>) tokens.

template <typename T>      

模闆定義以關鍵字 template 開始,

後接模闆形參表,模闆形參表是用尖括号包覆的一個或多個模闆形參的清單,形參之間以逗号分隔。

C++模闆(c++ primer)

The template parameter list cannot be empty.

模闆形參表不能為空。

Template Parameter List
模闆形參表

The template parameter list acts much like a function parameter list. A function parameter list defines local variable(s) of a specified type but leaves those variables uninitialized. At run time, arguments are supplied that initialize the parameters.

模闆形參表很像函數形參表,函數形參表定義了特定類型的局部變量但并不初始化那些變量,在運作時再提供實參來初始化形參。

Analogously, template parameters represent types or values we can use in the definition of a class or function. For example, ourcompare function declares one type parameter namedT. Inside compare, we can use the name T to refer to a type. Whichactual type T represents is determined by the compiler based on how the function is used.

同樣,模闆形參表示可以在類或函數的定義中使用的類型或值。例如,compare 函數聲明一個名為 T 的類型形參。在compare 内部,可以使用名字T 引用一個類型,T 表示哪個實際類型由編譯器根據所用的函數而确定。

A template parameter can be a type parameter, which represents a type, or anontype parameter, which represents a constant expression. A nontype parameter is declared following a type specifier. We'll see more about nontype parameters inSection 16.1.5 (p.632). A type parameter is defined following the keywordclass or typename. For example, class T is a type parameter namedT. There is no difference betweenclass and typename in this context.

模闆形參可以是表示類型的類型形參,也可以是表示常量表達式的非類型形參。非類型形參跟在類型說明符之後聲明,第 16.1.5 節将進一步介紹非類型形參。類型形參跟在關鍵字class 或 typename 之後定義,例如,class T 是名為T 的類型形參,在這裡class 和 typename 沒有差別。

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), itinstantiates 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.

使用函數模闆時,編譯器會推斷哪個(或哪些)模闆實參綁定到模闆形參。一旦編譯器确定了實際的模闆實參,就稱它執行個體化了函數模闆的一個執行個體。實質上,編譯器将确定用什麼類型代替每個類型形參,以及用什麼值代替每個非類型形參。推導出實際模闆實參後,編譯器使用實參代替相應的模闆形參産生編譯該版本的函數。編譯器承擔了為我們使用的每種類型而編寫函數的單調工作。

Given the calls

對于以下調用

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 replacesT byint and a second version that uses string in place ofT.

編譯器将執行個體化 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 thetemplate 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&);
      

Exercises Section 16.1.1

Exercise 16.1:

Write a template that returns the absolute value of its parameter. Call the template on values of at least three different types. Note: until we discuss how the compiler handles templateinstantiation inSection 16.3 (p.643), you should put each template definition and all uses of that template in the same file.

編寫一個模闆傳回形參的絕對值。至少用三種不同類型的值調用模闆。注意:在第 16.3 節讨論編譯器怎樣處理模闆執行個體化之前,你應該将每個模闆定義和該模闆的所有使用放在同一檔案中。

Exercise 16.2:

Write a function template that takes a reference to an ostream and a value, and writes the value to the stream. Call the function on at least four different types. Test your program by writing tocout, to a file, and to astringstream.

編寫一個函數模闆,接受一個 ostream 引用和一個值,将該值寫入流。用至少四種不同類型調用函數。通過寫至cout、寫至檔案和寫至stringstream 來測試你的程式。

Exercise 16.3:

When we called compare on two strings, we passed twostring objects, which we initialized from string literals. What would happen if we wrote:

當調用兩個 string 對象的 compare 時,傳遞用字元串字面值初始化的兩個 string 對象。如果編寫以下代碼會發生什麼?

compare ("hi", "world");
      

16.1.2. Defining a Class Template

16.1.2. 定義類模闆

Just as we can define function templates, we can also define class templates.

就像可以定義函數模闆一樣,也可以定義類模闆。

C++模闆(c++ primer)

To illustrate class templates, we'll implement our own version of the standard libraryqueue (Section 9.7, p.348) class. User programs ought to use the standardqueue class, not the one we define here.

為了舉例說明類模闆,我們将為标準庫 queue 類(第 9.7 節)實作一個自己的版本。使用者程式應使用标準的queue 類,而不是我們這裡定義的這個Queue 類。

Our Queue must be able to hold objects of different types, so we'll define it as aclass template. The operations ourQueue will support are a subset of the interface of the standardqueue:

我們自定義的 Queue 類必須能夠支援不同類型的對象,是以将它定義為類模闆。Queue 類将支援的操作是标準queue 類接口的子集:

  • push to add an item to the back of the queue

    push 操作,在隊尾增加一項

  • pop to remove the item at the head of the queue

    pop 操作,從隊頭删除一項

  • front to return a reference to the element at the head of the queue

    front 操作,傳回隊頭元素的引用

  • empty to indicate whether there are any elements in the queue

    empty 操作,指出隊列中是否有元素

We'll look at how we might implement our Queue in Section 16.4 (p. 647), but we can start by defining its interface:

第 16.4 節将介紹怎樣實作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 keywordtemplate followed by a template parameter list. OurQueue template takes a single template type parameter namedType.

類模闆也是模闆,是以必須以關鍵字 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 useType to name the return type from the overloadedfront operations and as the parameter type for thepush 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 ourQueue class replacingType by the specified actual type provided by the user. In this case, the compiler will instantiate three classes: a version ofQueue withType replaced by int, a second Queue class that usesvector<double> in place ofType, and a third that replaces Type by string.

編譯器使用實參來執行個體化這個類的特定類型版本。實質上,編譯器用使用者提供的實際特定類型代替 Type,重新編寫Queue 類。在這個例子中,編譯器将執行個體化三個Queue 類:第一個用 int 代替 Type,第二個用vector<double> 代替Type,第三個用 string 代替 Type。

Exercises Section 16.1.2

Exercise 16.4:

What is a function template? What is a class template?

什麼是函數模闆?什麼是類模闆?

Exercise 16.5:

Define a function template to return the larger of two values.

定義一個函數模闆,傳回兩個值中較大的一個。

Exercise 16.6:

Similar to our a simplified version of queue, write a class template namedList that is a simplified version of the standardlist class.

類似于我們的 queue 簡化版本,編寫一個名為 List 的類模闆,作為标準 list 類的簡化版本。

16.1.3. Template Parameters

16.1.3. 模闆形參

As with a function parameter, the name chosen by the programmer for a template parameter has no intrinsic meaning. In our example, we namedcompare's template type parameterT, 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 toGlorp in thecompare function template will be resolved to the same type when the function is instantiated.

如果希望使用模闆形參所表示的類型或值,可以使用與對應模闆形參相同的名字。例如,compare 函數中所有的Glorp 引用将在該函數被執行個體化時确定為同一類型。

Template Parameter Scope
模闆形參作用域

The name of a template parameter can be used after it has been declared as a template parameter and until the end of the template declaration or definition.

模闆形參的名字可以在聲明為模闆形參之後直到模闆聲明或定義的末尾處使用。

Template parameters follow normal name-hiding rules. A template parameter with the same name as an object, function, or type declared in global scope hides the global name:

模闆形參遵循正常名字屏蔽規則。與全局作用域中聲明的對象、函數或類型同名的模闆形參會屏蔽全局名字:

typedef double T;
     template <class T> T calc(const T &a, const T &b)
     {
          // tmp has the type of the template parameter T
          // not that of the global typedef
          T tmp = a;
          // ...
          return tmp;
     }
      

The global typedef that defines T as double is hidden by the type parameter namedT. Thus,tmp is not a double. Instead, the type of tmp is whatever type gets bound to the template parameterT.

将 T 定義為 double 的全局類型型别名将被名為 T 的類型形參所屏蔽,是以,tmp 不是double 型,相反,tmp 的類型是綁定到模闆形參的任意類型。

Restrictions on the Use of a Template Parameter Name
使用模闆形參名字的限制

A name used as a template parameter may not be reused within the template:

用作模闆形參的名字不能在模闆内部重用。

template <class T> T calc(const T &a, const T &b)
     {
         typedef double T; // error: redeclares template parameter T
         T tmp = a;
         // ...
         return tmp;
     }
      

This restriction also means that the name of a template parameter can be used only once within the same template parameter list:

這一限制還意味着模闆形參的名字隻能在同一模闆形參表中使用一次:

// error: illegal reuse of template parameter name V
     template <class V, class V> V calc(const V&, const V&) ;
      

Of course, just as we can reuse function parameter names, the name of a template parameter can be reused across different templates:

當然,正如可以重用函數形參名字一樣,模闆形參的名字也能在不同模闆中重用:

// ok: reuses parameter type name across different templates
     template <class T> T calc (const T&, const T&) ;
     template <class T> int compare(const T&, const T&) ;
      
Template Declarations
模闆聲明

As with any other function or class, we can declare a template without defining it. A declaration must indicate that the function or class is a template:

像其他任意函數或類一樣,對于模闆可以隻聲明而不定義。聲明必須指出函數或類是一個模闆:

// declares compare but does not define it
     template <class T> int compare(const T&, const T&) ;
      

The names of the template parameters need not be the same across declarations and the definition of the same template:

同一模闆的聲明和定義中,模闆形參的名字不必相同。

// all three uses of calc refer to the same function template
     // forward declarations of the template
     template <class T> T calc(const T&, const T&) ;
     template <class U> U calc(const U&, const U&) ;
     // actual definition of the template
     template <class Type>
     Type calc(const Type& a, const Type& b) { /* ... */ }
      

Each template type parameter must be preceded either by the keywordclass ortypename; each nontype parameter must be preceded by a type name. It is an error to omit the keyword or a type specifier:

每個模闆類型形參前面必須帶上關鍵字 class 或 typename,每個非類型形參前面必須帶上類型名字,省略關鍵字或類型說明符是錯誤的:

// error: must precede U by either typename or class
     template <typename T, U> T calc (const T&, const U&) ;
      

Exercises Section 16.1.3

Exercise 16.7:

Explain each of the following function template definitions and identify whether any are illegal. Correct each error that you find.

解釋下面每個函數模闆的定義并指出是否有非法的。改正所發現的錯誤。

(a) template <class T, U, typename V> void f1(T, U, V) ;
     (b) template <class T> T f2(int &T) ;
     (c) inline template <class T> T foo(T, unsigned int*) ;
     (d) template <class T> f4 (T, T) ;
     (e) typedef char Ctype ;
         template <typename Ctype> Ctype f5(Ctype a) ;
      
Exercise 16.8:

Explain which, if any, of the following declarations are errors and why.

如果有,解釋下面哪些聲明是錯誤的說明為什麼。

(a) template <class Type> Type bar(Type, Type) ;
         template <class Type> Type bar(Type, Type) ;
     (b) template <class T1, class T2> void bar(T1, T2) ;
         template <class C1, typename C2> void bar(C1, C2) ;
      
Exercise 16.9:

Write a template that acts like the library find algorithm. Your template should take a single type parameter that will name the type for a pair of iterators that should be parameters to the function. Use your function to find a given value in a vector<int> and in a list<string>.

編寫行為類似于标準庫中 find 算法的模闆。你的模闆應接受一個類型形參,該形參指定函數形參(一對疊代器)的類型。使用你的函數在vector<int> 和list<string> 中查找給定值。

16.1.4. Template Type Parameters

16.1.4. 模闆類型形參

Type parameters consist of the keyword class or the keywordtypename followed by an identifier. In a template parameter list, these keywords have the same meaning: They indicate that the name that follows represents a type.

類型形參由關鍵字 class 或 typename 後接說明符構成。在模闆形參表中,這兩個關鍵字具有相同的含義,都指出後面所接的名字表示一個類型。

A template type parameter can be used as a type specifier anywhere in the template, in exactly the same way as a built-in or class type specifier. In particular, it can be used to name the return type or a function parameter type, and for variable declarations or casts inside the function body:

模闆類型形參可作為類型說明符在模闆中的任何地方,與内置類型說明符或類類型說明符的使用方式完全相同。具體而言,它可以用于指定傳回類型或函數形參類型,以及在函數體中用于變量聲明或強制類型轉換。

// ok: same type used for the return type and both parameters
     template <class T> T calc (const T& a, const T& b)
     {
          // ok: tmp will have same type as the parameters & return type
          T tmp = a;
          // ...
          return tmp;
     }
      
Distinction Between typename and class
typename 與 class 的差別

In a function template parameter list, the keywords typename andclass have the same meaning and can be used interchangeably. Both keywords can be used in the same template parameter list:

在函數模闆形參表中,關鍵字 typename 和 class 具有相同含義,可以互換使用,兩個關鍵字都可以在同一模闆形參表中使用:

// ok: no distinction between typename and class in template parameter list
     template <typename T, class U> calc (const T&, const U&);
      

It may seem more intuitive to use the keyword typename instead of the keywordclass to designate a template type parameter; after all, we can use built-in (nonclass types) types as the actual type parameter. Moreover,typename more clearly indicates that the name that follows is a type name. However, the keywordtypename was added to C++ as part of Standard C++, so older programs are more likely to use the keywordclass exclusively.

使用關鍵字 typename 代替關鍵字 class 指定模闆類型形參也許更為直覺,畢竟,可以使用内置類型(非類類型)作為實際的類型形參,而且,typename 更清楚地指明後面的名字是一個類型名。但是,關鍵字typename 是作為标準 C++ 的組成部分加入到 C++ 中的,是以舊的程式更有可能隻用關鍵字class。

Designating Types inside the Template Definition
在模闆定義内部指定類型

In addition to defining data or function members, a class may define type members. For example, the library container classes define various types, such assize_type, that allow us to use the containers in a machine-independent way. When we want to use such types inside a function template, we must tell the compiler that the name we are using refers to a type. We must be explicit because the compiler (and a reader of our program) cannot tell by inspection when a name defined by a type parameter is a type or a value. As an example, consider the following function:

除了定義資料成員或函數成員之外,類還可以定義類型成員。例如,标準庫的容器類定義了不同的類型,如 size_type,使我們能夠以獨立于機器的方式使用容器。如果要在函數模闆内部使用這樣的類型,必須告訴編譯器我們正在使用的名字指的是一個類型。必須顯式地這樣做,因為編譯器(以及程式的讀者)不能通過檢查得知,由類型形參定義的名字何時是一個類型何時是一個值。例如,考慮下面的函數:

template <class Parm, class U>
     Parm fcn(Parm* array, U value)
     {
         Parm::size_type * p; // If Parm::size_type is a type, then a declaration
                              // If Parm::size_type is an object, then multiplication
     }
      

We know that size_type must be a member of the type bound toParm, but we do not know whethersize_type is the name of a type or a data member. By default, the compiler assumes that such names name data members, not types.

我們知道 size_type 必定是綁定到 Parm 的那個類型的成員,但我們不知道 size_type 是一個類型成員的名字還是一個資料成員的名字,預設情況下,編譯器假定這樣的名字指定資料成員,而不是類型。

If we want the compiler to treat size_type as a type, then we must explicitly tell the compiler to do so:

如果希望編譯器将 size_type 當作類型,則必須顯式告訴編譯器這樣做:

template <class Parm, class U>
     Parm fcn(Parm* array, U value)
     {
         typename Parm::size_type * p; // ok: declares p to be a pointer
     }
      

We tell the compiler to treat a member as a type by prefixing uses of the member name with the keywordtypename. By writingtypename Parm::size_type we say that membersize_type of the type bound toParm is the name of a type. Of course, this declaration puts an obligation on the types used to instantiatefcn: Those types must have a member namedsize_type that is a type.

通過在成員名前加上關鍵字 typename 作為字首,可以告訴編譯器将成員當作類型。通過編寫 typename parm::size_type,指出綁定到Parm 的類型的size_type 成員是類型的名字。當然,這一聲明給用執行個體化 fcn 的類型增加了一個職責:那些類型必須具有名為size_type 的成員,而且該成員是一個類型。

C++模闆(c++ primer)

If there is any doubt as to whether typename is necessary to indicate that a name is a type, it is a good idea to specify it. There is no harm in specifyingtypename before a type, so if thetypename was unnecessary, it won't matter.

如果拿不準是否需要以 typename 指明一個名字是一個類型,那麼指定它是個好主意。在類型之前指定 typename 沒有害處,是以,即使 typename 是不必要的,也沒有關系。

Exercises Section 16.1.4

Exercise 16.10:

What, if any, are the differences between a type parameter that is declared as atypename and one that is declared as aclass?

聲明為 typename 的類型形參與聲明為 class 的類型形參有差別嗎?差別在哪裡?

Exercise 16.11:

When must typename be used?

何時必須使用 typename?

Exercise 16.12:

Write a function template that takes a pair of values that represent iterators of unknown type. Find the value that occurs most frequently in the sequence.

編寫一個函數模闆,接受表示未知類型疊代器的一對值,找出在序列中出現得最頻繁的值。

Exercise 16.13:

Write a function that takes a reference to a container and prints the elements in that container. Use the container'ssize_type andsize members to control the loop that prints the elements.

編寫一個函數,接受一個容器的引用并列印該容器的元素。使用容器的 size_type 和 size 成員控制列印元素的循環。

Exercise 16.14:

Rewrite the function from the previous exercise to use iterators returned frombegin andend to control the loop.

重新編寫上題的函數,使用從 begin 和 end 傳回的疊代器來控制循環。

16.1.5. Nontype Template Parameters

16.1.5. 非類型模闆形參

A template parameter need not be a type. In this section we'll look at nontype parameters as used by function templates. We'll look at nontype parameters for class templates inSection 16.4.2 (p. 655) after we've seen more about how class templates are implemented.

模闆形參不必都是類型。本節将介紹函數模闆使用的非類型形參。在介紹了類模闆實作的更多内容之後,第 16.4.2 節将介紹類模闆的非類型形參。

Nontype parameters are replaced by values when the function is called. The type of that value is specified in the template parameter list. For example, the following function template declaresarray_init as a function template with one type and one nontype template parameter. The function itself takes a single parameter, which is a reference to an array (Section 7.2.4, p.240):

在調用函數時非類型形參将用值代替,值的類型在模闆形參表中指定。例如,下面的函數模闆聲明了 array_init 是一個含有一個類型模闆形參和一個非類型模闆形參的函數模闆。函數本身接受一個形參,該形參是數組的引用(第 7.2.4 節):

// initialize elements of an array to zero
     template <class T, size_t N> void array_init(T (&parm)[N])
     {
         for (size_t i = 0; i != N; ++i) {
             parm[i] = 0;
         }
     }
      

A template nontype parameter is a constant value inside the template definition. A nontype parameter can be used when constant expressions are requiredfor example, as we do hereto specify the size of an array.

模闆非類型形參是模闆定義内部的常量值,在需要常量表達式的時候,可使用非類型形參(例如,像這裡所做的一樣)指定數組的長度。

When array_init is called, the compiler figures out the value of the nontype parameter from the array argument:

當調用 array_init 時,編譯器從數組實參計算非類型形參的值:

int x[42];
     double y[10];
     array_init(x);  // instantiates array_init(int(&)[42]
     array_init(y);  // instantiates array_init(double(&)[10]
      

The compiler will instantiate a separate version of array_init for each kind of array used in a call toarray_init. For the program above, the compiler instantiates two versions ofarray_init: The first instance has its parameter bound to int[42], and in the other, that parameter is bound todouble[10].

編譯器将為 array_init 調用中用到的每種數組執行個體化一個 array_init 版本。對于上面的程式,編譯器将執行個體化 array_init 的兩個版本:第一個執行個體的形參綁定到int[42],另一個執行個體中的形參綁定到double[10]。

Type Equivalence and Nontype Parameters
類型等價性與非類型形參

Expressions that evaluate to the same value are considered equivalent template arguments for a template nontype parameter. The following calls toarray_init both refer to the same instantiation,array_init<int, 42>:

對模闆的非類型形參而言,求值結果相同的表達式将認為是等價的。下面的兩個 array_init 調用引用的是相同的執行個體——array_init<int, 42>:

int x[42];
     const int sz = 40;
     int y[sz + 2];
     array_init(x);  // instantiates array_init(int(&)[42])
     array_init(y);  // equivalent instantiation
      

Exercises Section 16.1.5

Exercise 16.15:

Write a function template that can determine the size of an array.

編寫可以确定數組長度的函數模闆。

Exercise 16.16:

Rewrite the printValues function from page 240 as a function template that could be used to print the contents of arrays of varying sizes.

将第 7.2.4 節的printValues 函數重新編寫為可用于列印不同長度數組内容的函數模闆。

16.1.6. Writing Generic Programs

16.1.6. 編寫泛型程式

When we write a template, the code may not be overtly type-specific, but template code always makes some assumptions about the types that will be used. For example, although ourcompare function is technically valid for any type, in practice the instantiated version might be illegal.

編寫模闆時,代碼不可能針對特定類型,但模闆代碼總是要對将使用的類型做一些假設。例如,雖然 compare 函數從技術上說任意類型都是有效的,但實際上,執行個體化的版本可能是非法的。

Whether the generated program is legal depends on the operations used in the function and the operations supported by the type or types used. Ourcompare function has has three statements:

産生的程式是否合法,取決于函數中使用的操作以及所用類型支援的操作。compare 函數有三條語句:

if (v1 < v2) return -1; // < on two objects of type T
     if (v2 < v1) return 1;  // < on two objects of type T
     return 0;               // return int; not dependent on T
      

The first two statements contain code that implicitly depends on the parameter type. Theif tests use the< operator on the parameters. The type of those parameters isn't known until the compiler sees a call tocompare andT is bound to an actual type. Which < operator is used depends entirely on the argument type.

前兩條語句包含隐式依賴于形參類型的代碼,if 測試對形參使用 < 操作符,直到編譯器看見compare 調用并且T 綁定到一個實際類型時,才知道形參的類型,使用哪個 < 操作符完全取決于實參類型。

If we call compare on an object that does not support the< operator, then the call will be invalid:

如果用不支援 < 操作符的對象調用 compare,則該調用将是無效的:

Sales_item item1, item2;
     // error: no < on Sales_item
     cout << compare(item1, item2) << endl;
      

The program is in error. The Sales_item type does not define the< operator, so the program won't compile.

程式會出錯。Sales_item 類型沒有定義 < 操作符,是以該程式不能編譯。

C++模闆(c++ primer)

The operations performed inside a function template constrains the types that can be used to instantiate the function. It is up to the programmer to guarantee that the types used as the function arguments actually support any operations that are used, and that those operations behave correctly in the context in which the template uses them.

在函數模闆内部完成的操作限制了可用于執行個體化該函數的類型。程式員的責任是,保證用作函數實參的類型實際上支援所用的任意操作,以及保證在模闆使用哪些操作的環境中那些操作運作正常。

Writing Type-Independent Code
編寫獨立于類型的代碼

The art of writing good generic code is beyond the scope of this language primer. However, there is one overall guideline that is worth noting.

編寫良好泛型代碼的技巧超出了本書的範圍,但是,有個一般原則值得注意。

C++模闆(c++ primer)

When writing template code, it is useful to keep the number of requirements placed on the argument types as small as possible.

編寫模闆代碼時,對實參類型的要求盡可能少是很有益的。

Simple though it is, our compare function illustrates two important principles for writing generic code:

雖然簡單,但它說明了編寫泛型代碼的兩個重要原則:

  • The parameters to the template are const references.

    模闆的形參是 const 引用。

  • The tests in the body use only < comparisons.

    函數體中的測試隻用 < 比較。

By making the parameters const references, we allow types that do not allow copying. Most typesincluding the built-in types and, except for the IO types, all the library types we've useddo allow copying. However, there can be class types that do not allow copying. By making our parameters const references, we ensure that such types can be used with ourcompare function. Moreover, ifcompare is called with large objects, then this design will also make the function run faster.

通過将形參設為 const 引用,就可以允許使用不允許複制的類型。大多數類型(包括内置類型和我們已使用過的除 IO 類型之外的所有标準庫的類型)都允許複制。但是,也有不允許複制的類類型。将形參設為const 引用,保證這種類型可以用于compare 函數,而且,如果有比較大的對象調用 compare,則這個設計還可以使函數運作得更快。

Some readers might think it would be more natural for the comparisons to be done using both the< and> operators:

一些讀者可能認為使用 < 和 > 操作符兩者進行比較會更加自然:

// expected comparison
     if (v1 < v2) return -1;
     if (v1 > v2) return 1;
     return 0;
      

However, by writing the code as

但是,将代碼編寫為

// expected comparison
     if (v1 < v2) return -1;
     if (v2 < v1) return 1; // equivalent to v1 > v2
     return 0;
      

we reduce the requirements on types that can be used with our compare function. Those types must support <, but they need not also support>.

可以減少對可用于 compare 函數的類型的要求,這些類型必須支援 <,但不必支援 >。

Exercises Section 16.1.6

Exercise 16.17:

In the "Key Concept" box on page 95, we noted that as a matter of habit C++ programmers prefer using != to using<. Explain the rationale for this habit.

在第 3.3.2 節的“關鍵概念”中,我們注意到,C++ 程式員習慣于使用!= 而不用<,解釋這一習慣的基本原理。

Exercise 16.18:

In this section we noted that we deliberately wrote the test incompare to avoid requiring a type to have both the< and > operators. On the other hand, we tend to assume that types will have both== and!=. Explain why this seeming discrepancy in treatment actually reflects good programming style.

本節中我們提到應該慎重地編寫 compare 中的資訊論以避免要求類型同時具有 < 和 > 操作符,另一方面,往往假定類型既有 == 又有 !=。解釋為什麼這一看似不一緻的處理實際上反映了良好的程式設計風格。

繼續閱讀