Function Templates

32
Function Templates • 前前 • 前前前前前前 • 前前前前前前前前 • Template Argument Deduction • Explict Template Arguments • Template Compilation Models • Template Explicit Specialization • Overloading Function Templates

description

Function Templates. 前言 定義函式樣板 函式樣板的實例化 Template Argument Deduction Explict Template Arguments Template Compilation Models Template Explicit Specialization Overloading Function Templates. 前言. C++ 是一個 strongly typed 的電腦語言,這使得我們必須為用意相同但參數資料型態不同的函式,寫出個別的定義。譬如: - PowerPoint PPT Presentation

Transcript of Function Templates

Page 1: Function Templates

Function Templates

• 前言• 定義函式樣板• 函式樣板的實例化• Template Argument Deduction

• Explict Template Arguments

• Template Compilation Models

• Template Explicit Specialization

• Overloading Function Templates

Page 2: Function Templates

前言C++ 是一個 strongly typed 的電腦語言,這使得我們必須為用意相同但參數資料型態不同的函式,寫出個別的定義。譬如:

int min (int x, int y) { return (x < y)? x : y }

double min (double x, double y) { return (x < y)? x : y }

查看上面兩個 min 函式,你不難發現到它們的程式碼除了傳回值和參數的資料型態有所不同以外,其它都一樣。

基於以上的觀察, C++ 提供資料型態參數化的函式樣板( func

tion templates )。有了函式樣板, C++ 編譯器會依據函式呼叫時的引數資料型態來自動產生所需的函式定義。

Page 3: Function Templates

定義函式樣板函式樣板的定義格式如下:

template <type parameter list>

function definition

其中 type parameter list 的項目可以是:

• type parameter:

• typename type_parameter_name (ANSI C++)

• class type_parameter_name ( 舊式的 C++)

• nontype parameter: type constant_name

項目之間必須用逗號區隔開來。

Page 4: Function Templates

範例:

以前面的 min 函式為例,我們可以寫出底下的函式樣板:

template <typename T>

T min (T x, T y)

{

return (x < y)? x : y

}

template <class T>

T min (T x, T y)

{

return (x < y)? x : y

}

其中的 T 是函式樣板的資料型態參數。你可以用任何合法的C++ 名稱來取代 T ,如 S, Type, …, 等等。

Page 5: Function Templates

範例:

template <typename T, int size>

T min (const T (&a)[size])

{

T min_val = a[0];

for (int i = 1; i < size; i++)

if (a[i] < min_val)

min_val = a[i];

return min_val;

}

函式樣板的非資料型態參數是用來指定出現在函式樣板定義中的常數值。

Page 6: Function Templates

In many ways, templates work like preprocessor macros, replacing the templated variable with the given type. However, there are many differences between a macro like this:

#define min(i, j) (((i) < (j)) ? (i) : (j))

and a template:

template<typename T> T min (T i, T j) { return ((i < j) ? i : j) }

Here are some problems with the macro:

There is no way for the compiler to verify that the macro parameters are of compatible types. The macro is expanded without any special type checking.

Page 7: Function Templates

The i and j parameters are evaluated twice. For example, if either parameter has a post-incremented variable, the increment is performed two times. For example, after the evaluations:

x = 5; y = 6;z = min(x++, y);

variable x becomes 7, not 6.

Because macros are expanded by the preprocessor, compiler error messages will refer to the expanded macro, rather than the macro definition itself. Also, the macro will show up in expanded form during debugging.

Page 8: Function Templates

函式樣板的實例化當呼叫函式樣板時, C++ 編譯器會依據引數的資料型態,自動產生出所需的函式,這個過程稱為實例化( instantiate )。

template <typename T>

T min (T x, T y)

{

return (x < y)? x : y

}

int m, x, y;

m = min(x, y);

int min (int x, int y)

{

return (x < y)? x : y

}

T int

Page 9: Function Templates

template <typename T, int size>

T min (const T (&a)[size])

{

T min_val = a[0];

for (int i = 0; i < size; i++)

if (a[i] < min_val)

min_val = a[i];

return min_val;

}

範例: int ia[] = { 5, 2, 8, 3, 9};

int m = min(ia);

int min (const int (&a)[5]){

…}

double da[] = { 5.0, 2.0, 8.3};

double m = min(da);

double min (const double (&a)[3]){

…}

T int, size 5

T double, size 3

Page 10: Function Templates

把函式樣板指定給函式指標時, C++ 編譯器也會依據引數的資料型態,自動產生出所需的函式。

template <typename T>

T min (T x, T y)

{

return (x < y)? x : y

}

int (*f) (int, int);

f = min;

int min (int x, int y)

{

return (x < y)? x : y

}

T int

Page 11: Function Templates

Template Argument Deduction

如前所述, C++ 編譯器從函式樣板呼叫時的引數型態來產生真實的函式。如果引數的型態和函式樣板的參數型態並不一致時, C++ 編譯器就必須根據一些規則來推導出合適的具體函式。這個過程稱之為 Template Argument Deduction

(樣板引數的演繹)。

底下我們用例子來說明這些演繹的規則。

Page 12: Function Templates

演繹時只檢視引數的資料型態,而不考慮傳回值的資料型態。

template <typename T>

T min (T x, T y) { return (x < y)? x : y }

double d = min(3, 4); // 產生 int min (int, int)

int m = min(3.0, 4.0); // 產生 double min (double , double )

int *ip = min(3, 4); // 產生 int min (int, int) ,但由於 int 不 // 可指定給 int * ,所以此行會造成 // 編譯的錯誤。

Page 13: Function Templates

如果引數資料型態與函式樣板的參數資料型態不一致,則可能會造成編譯上的錯誤。

template <typename T, int size>

T min (const T (&a)[size]) { … }

void f (int pval[9])

{

// error: Type (&)[] != int *

int jval = min(pval);

}

Page 14: Function Templates

template <typename T>

T min (T x, T y) { return (x < y)? x : y }

unsigned int ui;

// error: cannot instantiate min(unsigned int, int)

// must be: min(int, int) or

// min(unsigned int, unsigned int)

int m = min(ui, 1024);

Page 15: Function Templates

由於陣列可視為指標的一種,因此 C++ 編譯器允許引數資料型態是陣列,而函式樣板的參數資料型態是指標。

template <typename T>

T min (T *a, int size) { … }

int ia[] = { 5, 2, 8, 3, 9};

int m = min(ia, 5); // 產生 int min(int *, int)

double da[10];

double d = min(da, 10); // 產生 double min(double *, int)

Page 16: Function Templates

演繹時不考慮函式樣板資料型態參數中的 const 和 volatile

宣告。

template <typename T>

T min (const T *a, int size) { … }

int *ia = new int[10];

int m = min(ia, 10); // 產生 int min(const int *, int)

double *da = new double[10];

// 產生 double min(const double *, int)

double d = min(da, 10);

Page 17: Function Templates

Explicit Template Arguments

當無法決定如何從函式樣板產生具體函式時、或需要自定產生某種資料型態的具體函式時,我們可以在呼叫函式樣板時,明白地指定出資料型態參數的種類。指定的格式如下:

template_func<explicit type list>(argument list);

譬如:

template <typename T>

T min (T x, T y) { return (x < y)? x : y }

unsigned int ui;

// min(unsigned int, unsigned int)

unsigned int m = min<unsigned int>(ui, 1024);

Page 18: Function Templates

範例:

template < typename RT, typename T1, typename T2>

RT sum (T1 x, T2 y) { … }

typedef unsigned int ui_type;

char ch;

ui_type ui;

// error: RT can’t be deduced.

ui_type loc1 = sum(ch, ui);

// ok: ui_type sum(char, ui_type)

ui_type loc1 = sum<ui_type, char, ui_type>(ch, ui);

Page 19: Function Templates

範例:

template < typename RT, typename T1, typename T2>

RT sum (T1 x, T2 y) { … }

void manipulate(int (*pf)(int, char));

void manipulate(double (*pf)(float, float));

// error: which instantiation of sum?

manipulate(sum);

// ok

manipulate(sum<double, float, float>);

Page 20: Function Templates

template < typename RT, typename T1, typename T2>

RT sum (T1 x, T2 y) { … }

typedef unsigned int ui_type;

char ch;

ui_type ui;

// ok: ui_type sum(char, ui_type)// T1 和 T2 可由引數的資料型態推演得到

ui_type loc1 = sum<ui_type>(ch, ui);

// ok: ui_type sum(char, ui_type)// T1 和 T2 可由 pf 的引數資料型態推演得到

ui_type (*pf)(char, ui_type) = sum<ui_type>;

若不會造成混淆的話, < 和 > 之間的資料類型有些可以省略。

Page 21: Function Templates

Template Compilation Models

C++ 編譯器可能提供下列三種把函式樣板實例化的方式:

Inclusion Compilation

把函式樣板的定義寫在 .h 標頭檔之中,然後在呼叫函式樣板的程式檔中把這個標頭檔用 include 的方式加進來,讓 C++ 編譯器得以進行函式樣板的 instantiation 。

Separation Compilation

把函式樣板的定義寫在一個 .cpp 程式檔之中,然後在呼叫函式樣板的程式檔中宣告此函式樣板。 C++ 編譯器會在編譯後,自動進行所需的 instantiation 。

Explicit Instantiation Declaration

由程式設計師指定所需的 instantiation 。

Page 22: Function Templates

Inclusion Compilation Model

把函式樣板的定義寫在 .h 標頭檔之中,然後在呼叫函式樣板的程式檔中用 include 的方式把這個標頭檔加進來。譬如:

// file: min.h

template <typename T>

T min (T x, T y)

{ return (x < y)? x : y }

// prog.cpp

#include “min.h”

int m = min(3, 4);

這個方法的優點是: C++ 編譯器可以直接地利用標頭檔中的函式樣板定義來進行 instantiation 的步驟。此外,現有的 C

++ 編譯器絕大部份都支援這個方法。

Page 23: Function Templates

上述方法的缺點是:

當有多個程式檔 include 定義函式樣板的 .h 標頭檔時, C

++ 編譯器會重複多次的 instantiations ,產生出許多個相同的函式定義,然而其中只有一個被保留下來,其它則捨棄不用。這顯然不是一個有效率的作法。

把函式樣板的定義寫在 .h 標頭檔而暴露在外,顯然不符合所謂 information hiding 的原則。

若函式樣板的定義又臭又長,又有多個程式檔 include 定義它的標頭檔時, C++ 編譯器必須重複地處理,無疑地也會增加 C++ 編譯器許多的負擔。

Page 24: Function Templates

Separation Compilation Model

把函式樣板的定義寫在一個 .cpp 程式檔之中,然後在呼叫函式樣板的程式檔中宣告此函式樣板。

// file: min.h

template <typename T>

T min (T x, T y) ; // declaration

// prog.cpp

#include “min.h”

int m = min(3, 4);// file: min.cpp

export template <typename T>

T min (T x, T y)

{ return (x < y)? x : y }

注意行首的關鍵字 export

Page 25: Function Templates

上述的方法雖然解決了 inclusion compilation 的缺點,然而許多不符合 ANSI 標準的舊 C++ 編譯器並不支援這個方法。儘管如此,只要你的 C++ 編譯器有支援的話,你還是應該使用這項 ANSI C++ 的新功能。

Page 26: Function Templates

Explicit Instantiation Declaration

在前面的兩種函式樣板編譯模式中,函式樣板只有在被呼叫後才會產生真實的函式。但在某些情況下,即使函式樣板沒有被呼叫也需要產生真實的函式(如:製作程式庫)。這時我們可以利用 explicit instantiation 的宣告。譬如:

template <typename T>

T sum (T op1, int op2) { … }

// explicit instantiation declaration

template int* sum<int *> (int *, int);

產生 int* sum(int *, int);

Page 27: Function Templates

當函式樣板對某些資料型態不適用或效率不佳時,我們可以設計一個特殊化的函式樣板來取代。舉例來說,底下的函式樣板無法用來獲得較小的 C 字串:

template <typename T>

T min (T x, T y) { return (x < y)? x : y }

因此我們就寫一個特殊化的函式樣板如下:

template<> const char * min (const char *x, const char * y)

{ return strcmp(x,y) < 0? x : y; }

Template Explicit Specialization

Page 28: Function Templates

template <typename T>

T min (T x, T y) { return (x < y)? x : y }

template<> const char * min (const char *x, const char * y)

{ return strcmp(x,y) < 0? x : y; }

int d = min(2.0, 3.0); // 產生 double min(double, double)

int m = min(2, 3); // 產生 int min(int , int )

// 產生 const char * min(const char *, const char *)

const char *cp = min(“book”, “apple”);

Page 29: Function Templates

Overloading Function Templates

如一般函式,我們也可以定義超載函式樣板。譬如:

template <typename T>T min(const vector<T>&); // #1

template <typename T>T min(const T*, int); // #2

template <typename T>T min(T, T); // #3

// ordinary functionconst char *min(const char *, const char *); // #4

Page 30: Function Templates

vector<int> iv(1024);int m1 = min(iv); // #1 instantiation

int ia[1024];int m2 = min(ia, 1024); // #2 instantiation

double d = min(3.0, 4.0); // #3 instantiation

float f = min(3.0, 4.0); // #3 instantiation

int m3 = min(3, 4); // #3 instantiation

const char *cp = min(“Y”, “N”); // #4 instantiation

unsigned ui;int m4 = min(3, ui); // error

Page 31: Function Templates

範例:

template <typename T>T min(T, T);

template <typename T, typename U>T min(T, U);

unsigned ui;int m4 = min(3, ui); // ok

int m3 = min(3, 4); // error: ambiguous

Page 32: Function Templates

範例:

template <typename T> // #1, more generalT sum(T, int);

template <typename T> // #2, more specialT sum(T*, int);

int ia[1024];int m2 = sum(ia, 1024); // #2 instantiation