平時我們是怎樣寫Code的

假設我要寫一個計總和的function:

1
2
3
4
5
6
7
8
9
int sum(int a, int b) {
return a+b;
}

int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
}
// Outputs 22

這個program是正確的,但是function只限於integer.
雖然我大可以寫多幾個function,但這樣很麻煩。

編寫sum()能使用任何類型的參數的一個版本是否會更有效?
Function templates 使我們能夠做到這一點!

Function templates

使用Function templates,基本思想是避免為每個variable指定確切data type的必要。
C++為我們提供了使用佔位符類型(稱為template type parameters)來定義function。

Syntax :

1
2
template <class T> 
//We named our template type T, which is a generic data type.

示範:

1
2
3
4
5
6
7
8
9
10
11
template <class T>
T sum(T a, T b) {
return a+b;
}

int main () {
int x=7, y=15;
cout << sum(x, y) << endl; // Outputs 22
double xd=7.15, yd=15.54;
cout << sum(xd, yd) << endl; // Outputs 22.69
}

現在可以見到,他會自動對應double和integer。

另一例子:

When creating a template type parameter, the keyword typename may be used as an alternative to the keyword class: template .
In this context, the keywords are identical.

Function templates只需要編寫一次,並且可以使用不同的類型,因此可以節省大量時間,大大減少重複代碼,更減少了代碼維護。
使用Function templates的另一個優勢是增強了安全性,因為它不需要手動複製function和更改data type。

Function templates with Multiple Parameters

Function templates還使處理多種通用數據類型。 使用逗號分隔的列表定義數據類型。

現在我們寫一個function去比較各種數據類型(一個int和一個double)的Parameter,並return較小的Parameter。

1
2
template <class T, class U>
//this template declares two different generic data types, T and U.

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
//The expression (a < b ? a : b) is equivalent to the expression:
//if a is smaller than b, return a, else, return b.
}

int main () {
int x=72;
double y=15.34;
cout << smaller(x, y) << endl;
}

// Outputs 15

T是Type的縮寫,是Type參數的廣泛使用的名稱。
沒有說一定要使用T,您可以使用任何適合您的標識符來聲明類型參數(不能用C++ keywords)。

Class Templates

Class Templates Like function templates, class templates are useful when a class defines something that is independent of the data type.
Can be useful for classes like LinkedList, BinaryTree, Stack, Queue, Array, etc.

如果您在Class之外定義member function(例如在單獨的source file),則需要特定的語法。
您需要在Class名稱後的尖括號中指定通用類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;

template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b) { }
T bigger();
};

template <class T>
T Pair<T>::bigger() {
return (first>second ? first : second);
}

int main()
{
Pair <int> obj(11, 22);
cout << obj.bigger() << endl;
// Outputs 22
Pair <double> obj2(23.43, 5.68);
cout << obj2.bigger() << endl;
// Outputs 23.43
return 0;
}

Template Specialization

對於常規的Class template,Class處理不同Data Type的方式是相同的。 所有Data Type都運行相同的代碼。
當將特定Type作為template parameter傳遞時,Template Specialization允許不同Data Type運行不同的代碼。

要為數據類型char指定不同的行為,我們就要用到template specialization。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template <class T>
class MyClass {
public:
MyClass (T x) {
cout <<x<<" - not a char"<<endl;
}
};

/*
notice that we precede the class name with template<>, including an empty parameter list.
This is because all types are known and no template arguments are required for this specialization,
but still, it is the specialization of a class template, and thus it requires to be noted as such.
*/
template < >
class MyClass<char> {
public:
MyClass (char x) {
cout <<x<<" is a char!"<<endl;
}
};

/*
But more important than this prefix, is the <char> specialization parameter after the class template name.
This specialization parameter itself identifies the type for which the template class is being specialized (char).
In the example above, the first class is the generic template, while the second is the specialization.
If necessary, your specialization can indicate a completely different behavior from the behavior of the generic template.
*/

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;

template <class T>
class MyClass {
public:
MyClass (T x) {
cout <<x<<" - not a char"<<endl;
}
};

template < >
class MyClass<char> {
public:
MyClass (char x) {
cout <<x<<" is a char!"<<endl;
}
};

int main () {
MyClass<int> ob1(42);
MyClass<double> ob2(5.47);
MyClass<char> ob3('s');
/*
As you can see, the generic template worked for int and double.
However, our template specialization was invoked for the char data type.
*/
/*
Output:
42 - not a char
5.47 - not a char
s is a char!
*/
}

Keep in mind that there is no member “inheritance” from the generic template to the specialization,
so all members of the template class specializations must be defined on their own.

Reference

Templates in C++