C primer Templates and Generic Programming Overview Template

  • Slides: 38
Download presentation
C++ primer: Templates and Generic Programming 洪嘉楙

C++ primer: Templates and Generic Programming 洪嘉楙

Overview • Template 定義式 – Function template – Class template – Template type 參數

Overview • Template 定義式 – Function template – Class template – Template type 參數 – Template non-type 參數 – 編寫泛型程式(Generic Programs) • 具現化(Instantiation) – Function template 具現化 – Class template 具現化 – Template 引數推導 – Function template 的顯式引數(Explicit Arguments) • Class Template的成員 – Class template 的成員函式 – Non-type參數的template引數 – Class Template內的Friend宣告

Template 定義式 • Template主要運用在function及class上, 可使編譯器自行推導變數型態 • Template 使用的時機: // returns 0 if the values

Template 定義式 • Template主要運用在function及class上, 可使編譯器自行推導變數型態 • Template 使用的時機: // returns 0 if the values are equal, -1 if v 1 is smaller, 1 if v 2 is smaller int compare(const string &v 1, const string &v 2) { if (v 1 < v 2) return -1; if (v 2 < v 1) return 1; return 0; } int compare(const double &v 1, const double &v 2) { if (v 1 < v 2) return -1; if (v 2 < v 1) return 1; return 0; }

Function template • 與其為每個目標型別定義一個新函式,我們可以使用 template來定義此function: // implement strcmp()-like generic compare function // returns 0

Function template • 與其為每個目標型別定義一個新函式,我們可以使用 template來定義此function: // implement strcmp()-like generic compare function // returns 0 if the values are equal, 1 if v 1 is larger, -1 if v 1 is smaller template <typename T> int compare(const T &v 1, const T &v 2) { if (v 1 < v 2) return -1; if (v 2 < v 1) return 1; return 0; } ※Template 定義式以關鍵字template起首, 接續<typename T> T為template參數

Function template (cont. ) • 使用Function template: int main (){ // T is int;

Function template (cont. ) • 使用Function template: 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 s 1 = "hi", s 2 = "world"; cout << compare(s 1, s 2) << endl; return 0; }

Function template (cont. 2) • function template被使用時,compiler會推導什麼樣的 template引數綁定於template參數( T )上, 一判別出 template引數後, function template將會被具現(instantiate)

Function template (cont. 2) • function template被使用時,compiler會推導什麼樣的 template引數綁定於template參數( T )上, 一判別出 template引數後, function template將會被具現(instantiate) 出一份實體。 • Inline function template宣告: 關鍵字inline放在template參 數列之後、返回類型之前,不能放在關鍵字 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&);

Class template (cont. ) • 定義Queue介面(interface): template <class Type> class Queue { public: Queue

Class template (cont. ) • 定義Queue介面(interface): 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: //. . . };

Class template (cont. 2) • 與function template不同, 使用class template時我們必須明 確指出template參數的引數 • 使用class template: Queue<int>

Class template (cont. 2) • 與function template不同, 使用class template時我們必須明 確指出template參數的引數 • 使用class template: Queue<int> qi; // Queue that holds ints Queue<string> qs; // Queue that holds strings Queue< vector<double> > qc; // Queue that holds vectors of doubles ※compiler使用指定的引數具現出class template的特定版本, 實際上 compiler將Queue重新寫過以用戶指定的實際型別替換, 具現出三 個Queue classes: Int. vector<double> 及 string.

Template 參數 • <typename T>: 用以區別”T”為type參數或是non-type參數。若它是個type參數, 表現的是一個未知型別; 若它是個non-type參數表現的是一個未 知數值 • <typename T>: 無本質意義, 如同函式參數一般

Template 參數 • <typename T>: 用以區別”T”為type參數或是non-type參數。若它是個type參數, 表現的是一個未知型別; 若它是個non-type參數表現的是一個未 知數值 • <typename T>: 無本質意義, 如同函式參數一般 • Template參數作用域: 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; }

Template 參數 (cont. ) • Template 參數名稱的使用限制: // template參數名稱不能在tamplate內被重複使用 template <class T> T calc(const

Template 參數 (cont. ) • Template 參數名稱的使用限制: // template參數名稱不能在tamplate內被重複使用 template <class T> T calc(const T &a, const T &b) { typedef double T; T tmp = a; //. . . return tmp; // 錯誤: 重複宣告 template parameter T } ------------------------------------------// 錯誤: illegal reuse of template parameter name V template <class V, class V> V calc(const V&, const V&) ; ------------------------------------------// 沒問題: 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 參數 (cont. 2) • Template宣告式: // declares compare but does not define it

Template 參數 (cont. 2) • Template宣告式: // declares compare but does not define it template <class T> int compare(const T&, const T&) ; ------------------------------------------// template的名稱於同一template的多個宣告式定義式間不需相同 // all the uses of calc refer to the same function template // template的前置宣告 template <class T> T calc(const T&, const T&) ; // template的實際定義 template <class Type> Type calc(const Type& a, const Type& b) { /*. . . */ } ------------------------------------------// error: must precede U by either typename or class template <typename T, U> T calc (const T&, const U&) ;

Template type 參數(cont. ) • 有些class可以定義型別成員, 如標準容器就定義了size_type獨立當做型別宣 告。因此如果我們想在template中使用這種型別, 就必須表明型別 • 在template參數式中表明型別: template <class Parm,

Template type 參數(cont. ) • 有些class可以定義型別成員, 如標準容器就定義了size_type獨立當做型別宣 告。因此如果我們想在template中使用這種型別, 就必須表明型別 • 在template參數式中表明型別: 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 confused!! } --------------------------------------template <class Parm, class U> Parm fcn(Parm* array, U value) { typename Parm: : size_type * p; // ok: declares p to be a pointer }

Template non-type 參數 • Template不僅能代表型別, 也可以表數值 • 在使用函數時non-type參數將用數值代替,數值的型別在template參 數列中指定: // 將array初始化為 0, size_t (unsigned

Template non-type 參數 • Template不僅能代表型別, 也可以表數值 • 在使用函數時non-type參數將用數值代替,數值的型別在template參 數列中指定: // 將array初始化為 0, size_t (unsigned int ) in C++ STL template <class T, size_t N> void array_init(T (&parm)[N]) { for (size_t i = 0; i != N; ++i) { parm[i] = 0; } } -------------------------------------------// 當調用 array_init 時,編譯器從陣列引數計算non-type參數的值: int x[42]; double y[10]; array_init(x); // instantiates array_init(int(&)[42] array_init(y); // instantiates array_init(double(&)[10]

編寫泛型程式(Generic Programs) • 編寫template時,不可能針對特定類型,但是要對使用資料型 別做一些假設: if (v 1 < v 2) return -1; if

編寫泛型程式(Generic Programs) • 編寫template時,不可能針對特定類型,但是要對使用資料型 別做一些假設: if (v 1 < v 2) return -1; if (v 2 < v 1) return 1; return 0; // < on two objects of type T // return int; not dependent on T • 如果用不支援” < “ 操作的物件呼叫 template functioncompare,則該呼叫將是無效的: Sales_item 1, item 2; // error: no < on Sales_item cout << compare(item 1, item 2) << endl; ※程式會出錯。Sales_item 類型沒有定義 “<“操作,所以該程式不能 編譯。

編寫泛型程式(Generic Programs) (cont. ) • 編寫泛型程式的原則: – 對template type的要求盡可能減少 – template type儘可能使用const reference ※對於”禁止copy的type”或是”size較大的type”相當有效

編寫泛型程式(Generic Programs) (cont. ) • 編寫泛型程式的原則: – 對template type的要求盡可能減少 – template type儘可能使用const reference ※對於”禁止copy的type”或是”size較大的type”相當有效 Ex: 一般使用 < 和 > 操作符兩者進行比較會更加自然: // expected comparison if (v 1 < v 2) return -1; if (v 1 > v 2) return 1; return 0; 但是,將其改編為: // expected comparison if (v 1 < v 2) return -1; if (v 2 < v 1) return 1; // equivalent to v 1 > v 2 return 0; 可以減少對可用於 compare 函數template type的要求

Overview • Template 定義式 – Function template – Class template – Template type 參數

Overview • Template 定義式 – Function template – Class template – Template type 參數 – Template non-type 參數 – 編寫泛型程式(Generic Programs) • 具現化(Instantiation) – Function template 具現化 – Class template 具現化 – Template 引數推導 – Function template 的顯式引數(Explicit Arguments) • Class Template的成員 – Class template 的成員函式 – Non-type參數的template引數 – Class Template內的Friend宣告

Function template 具現化 • 使用function template時,編譯器通常會為我們進行”引數推導”: int main() { compare(1, 0); compare(3. 14, 2.

Function template 具現化 • 使用function template時,編譯器通常會為我們進行”引數推導”: int main() { compare(1, 0); compare(3. 14, 2. 7); return 0; } // ok: binds template parameter to int // ok: binds template parameter to double • 經過以上的呼叫產生 compare 的兩個版本:一個用 int 代替 T,另一 個用double 代替 T: int compare(const int &v 1, const int &v 2) { int compare(const double &v 1, const double if (v 1 < v 2) return -1; &v 2) if (v 2 < v 1) return 1; { return 0; if (v 1 < v 2) return -1; } if (v 2 < v 1) return 1; return 0; }

Class template 具現化 • 以呼叫先前定義的” Queue”為例 : Queue<int> qi; • Compiler的具現化的結果: // simulated version

Class template 具現化 • 以呼叫先前定義的” Queue”為例 : Queue<int> qi; • Compiler的具現化的結果: // simulated version of Queue instantiated for type int template <class Type> class Queue<int> { public: Queue(); // this bound to Queue<int>* int &front(); // return type bound to int const int &front() const; // return type bound to int void push(const int &); // parameter type bound to int void pop(); // type invariant code bool empty() const; // type invariant code private: //. . . };

Template 引數推導 • 多個相同template type參數需與推導而得的實際型別必須完全匹配: ※若要使其同時符合引述推導原則, 則需使用不 template <typename T> 同的type參數: int compare(const T&

Template 引數推導 • 多個相同template type參數需與推導而得的實際型別必須完全匹配: ※若要使其同時符合引述推導原則, 則需使用不 template <typename T> 同的type參數: int compare(const T& v 1, const T& v 2) // argument types can differ, but must be compatible { template <typename A, typename B> if (v 1 < v 2) return -1; int compare(const A& v 1, const B& v 2) if (v 2 < v 1) return 1; { return 0; if (v 1 < v 2) return -1; } if (v 2 < v 1) return 1; int main() return 0; { } short si; compare(si, 1024); // 錯誤: 無法具現化compare(short, int) // must be: compare(short, short) or compare(int, int) return 0; }

Template 引數推導 (cnot. 2) • Template type參數與引數型別間的有限轉換實例: template <typename T> T fobj(T, T); //

Template 引數推導 (cnot. 2) • Template type參數與引數型別間的有限轉換實例: template <typename T> T fobj(T, T); // 引數將被複製 (non-reference) template <typename T> T fref(const T&, const T&); // reference 引數 string s 1(“a value”); const string s 2(“another value”); fobj(s 1, s 2); fref(s 1, s 2); int a[10], b[42]; fobj(a, b); fref(a, b); // ok: calls f(string, string), const 被忽略 // ok: non const 物件 s 1轉換為const reference // ok: calls f(int*, int*) // error: 矩陣型態不符; 變數無法轉變成pointer

Template 引數推導 (cnot. 3) • Non-tamplate 引數的一般轉換: template <class Type> Type sum(const Type &op

Template 引數推導 (cnot. 3) • Non-tamplate 引數的一般轉換: template <class Type> Type sum(const Type &op 1, int op 2) { return op 1 + op 2; } double d = 3. 14; string s 1("hiya"), s 2(" world"); sum(1024, d); // ok: 具現出sum(int, int), 將 d 轉換為 int sum(1. 4, d); // ok: 現出sum(double, int), 將 d 轉換為 int sum(s 1, s 2); // error: s 2 cannot be converted to int

Function template 的顯式引數 (Explicit Arguments) • 明確指定template引數: // T or U as the return

Function template 的顯式引數 (Explicit Arguments) • 明確指定template引數: // T or U as the return type? template <class T, class U> ? ? ? sum(T, U); // neither T nor U works as return type, L is “long” type sum(3, 4*L); // second type is larger; want U sum(T, U) sum(3*L, 4); // first type is larger; want T sum(T, U) //解決此問題的一個辦法,可將 sum 的引數將較小的類型強制轉 //換(第 5. 12. 4 節)為希望作為結果使用的類型: int i; short s; // ok: 不論T 或 U 當作return type都可以正常運作 sum(static_cast<int>(s), i); // ok: instantiates int sum(int, int)

Function template 的顯式引數 (Explicit Arguments) (cont. ) • 針對return type使用一個type參數: // T 1無法被推導出來, 因其並未出現於函式參數內

Function template 的顯式引數 (Explicit Arguments) (cont. ) • 針對return type使用一個type參數: // T 1無法被推導出來, 因其並未出現於函式參數內 template <class T 1, class T 2, class T 3> T 1 sum(T 2, T 3); //因此呼叫時必須明確指定return type: // ok T 1明確被指定; T 2 and T 3 inferred from argument types long val 3 = sum<long>(i, lng); // ok: calls long sum(int, long) • 顯式引數和pointer to function templates: template <typename T> int compare(const T&, const T&); // 重載 func; each take a different function pointer type void func(int(*) (const string&, const string&)); void func(int(*) (const int&, const int&)); func(compare<int>); // ok: 明確指出要哪一個版本

Overview • Template 定義式 – Function template – Class template – Template type 參數

Overview • Template 定義式 – Function template – Class template – Template type 參數 – Template non-type 參數 – 編寫泛型程式(Generic Programs) • 具現化(Instantiation) – Function template 具現化 – Class template 具現化 – Template 引數推導 – Function template 的顯式引數(Explicit Arguments) • Class Template的成員 – Class template 的成員函式 – Non-type參數的template引數 – Class Template內的Friend宣告

Class Template的成員 (cont. ) • Queue. Item實作: template <class Type> class Queue. Item {

Class Template的成員 (cont. ) • Queue. Item實作: template <class Type> class Queue. Item { // private class: no public section Queue. Item(const Type &t): item(t), next(0) { } Type item; // value stored in this element Queue. Item *next; // pointer to next element };

Class Template的成員 (cont. 2) • Queue實作: template <class Type> class Queue { public: //

Class Template的成員 (cont. 2) • Queue實作: template <class Type> class Queue { public: // empty Queue(): head(0), tail(0) { } // copy control to manage pointers to Queue. Items in the Queue(const Queue &Q): head(0), tail(0) { copy_elems(Q); } Queue& operator=(const Queue&); ~Queue() { destroy(); } // return element from head of Queue // unchecked operation: front on an empty Queue is undefined Type& front() { return head->item; } const Type &front() const { return head->item; } 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 return head == 0; } private: Queue. Item<Type> *head; // pointer to first element in Queue. Item<Type> *tail; // pointer to last element in Queue // utility functions used by copy constructor, assignment, and destructor void destroy(); // delete all the elements }

Class template 的成員函式 • 必須以關鍵字 template 起始,後接class的template參數列。 • 必須指出它是哪個class的成員。 • 上述class 名稱須包含template參數。 • 以POP()為例:

Class template 的成員函式 • 必須以關鍵字 template 起始,後接class的template參數列。 • 必須指出它是哪個class的成員。 • 上述class 名稱須包含template參數。 • 以POP()為例: template <class Type> void Queue<Type>: : pop() //表示位於class template Queue<Type>作用域內 { // pop is unchecked: Popping off an empty Queue is undefined Queue. Item<Type>* p = head; // 保存head目前所指, 稍後才可刪除 head = head->next; // 令head指向下一個元素 delete p; // delete old head element }

Non-type參數的template引數 • Class template的non-type引數: template <int hi, int wid> class Screen { public: //

Non-type參數的template引數 • Class template的non-type引數: template <int hi, int wid> class Screen { public: // template nontype parameters used to initialize data members Screen(): screen(hi * wid, '#'), cursor (0), height(hi), width(wid) { } //. . . private: std: : string screen; std: : string: : size_type cursor; std: : string: : size_type height, width; }; • 像任意class template一樣,使用 Screen 類型時必須顯式聲明形參值: Screen<24, 80> hp 2621; // screen 24 lines by 80 characters

Class Template內的Friend宣告 • 一般的non-template class或function宣告為friend, 便是將其存取權授予指定的class或function。 • 若將class或function template宣告為friend,便是 將其存取權賦予此friend 的所有具現體。 • 也可以指定「class template或function

Class Template內的Friend宣告 • 一般的non-template class或function宣告為friend, 便是將其存取權授予指定的class或function。 • 若將class或function template宣告為friend,便是 將其存取權賦予此friend 的所有具現體。 • 也可以指定「class template或function template 的某個特定具現體」為friend。

Class Template內的Friend宣告 (cont. ) • 一般的friends宣告: template <class Type> class Bar { // 提供存取權給一般的non-template

Class Template內的Friend宣告 (cont. ) • 一般的friends宣告: template <class Type> class Bar { // 提供存取權給一般的non-template class或function friend class Foo. Bar; friend void fcn(); //. . . }; //以上兩個宣告的意義是Foobar的成員函式以及fcn()函式 //可以存取Bar的任何具現體的private以及protected成員

Class Template內的Friend宣告 (cont. 2) • 全體性的template friend: template <class Type> class Bar { //提供存取權給Foo

Class Template內的Friend宣告 (cont. 2) • 全體性的template friend: template <class Type> class Bar { //提供存取權給Foo 1和 templ_fcn 1的任何具現體 template <class T> friend class Foo 1; template <class T> friend void templ_fcn 1(const T&); //. . . }; //本例的friend建立起每個Bar具現體與其friend: Foo 1和 //temp_fcn 1之間的一對多映射關係. 對於每個Bar具現體 //, Foo 1或temp_fcn 1的所有具現體都是friends

Class Template內的Friend宣告 (cont. 3) • 將存取權開放給某個template特定的具現體: template <class T> class Foo 2; template <class

Class Template內的Friend宣告 (cont. 3) • 將存取權開放給某個template特定的具現體: template <class T> class Foo 2; template <class T> void templ_fcn 2(const T&); template <class Type> class Bar { friend class Foo 2<char*>; // 存取權僅給以”char*為目標型別”的特定具現體 friend void templ_fcn 2<char*>(char* const &); //. . . }; -------------------------------------------template <class T> class Foo 3; template <class T> void templ_fcn 3(const T&); template <class Type> class Bar { // Bar的任何具現體都提供存取權給以相同目標型別具現出來的Foo 3或templ_fcn 3 friend class Foo 3<Type>; friend void templ_fcn 3<Type>(const Type&); //. . . };

More… • Member template • Handle template • Template specialize ….

More… • Member template • Handle template • Template specialize ….