0% found this document useful (0 votes)
73 views9 pages

9 CPP Template

The document discusses template and generic programming in C++. It provides examples of using the STL vector template class, which can store elements of different types, and function templates, which allow functions to operate on generic types that are specified during compilation. Template classes and functions allow writing code that is independent of specific data types, improving code reusability and reducing duplication.

Uploaded by

manju_achari_c
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
73 views9 pages

9 CPP Template

The document discusses template and generic programming in C++. It provides examples of using the STL vector template class, which can store elements of different types, and function templates, which allow functions to operate on generic types that are specified during compilation. Template classes and functions allow writing code that is independent of specific data types, improving code reusability and reducing duplication.

Uploaded by

manju_achari_c
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

yet another insignificant programming notes...

| HOME

TABLE OF CONTENTS (HIDE)


1. Introduction

C++ Programming Language


2. Example: STL's vector Template Class
3. Function Template
4. Class Template

Template and Generic 5. Example: MyComplex Template Class

Programming

1. Introduction
We are familiar in passing value/variable into function. Instead of passing a variable, we pass a type (such as int, double, and
Point) into template. Passing type is known as generic programming, as we can program in generic type and invoke the code using
a specific type.

The goal of generic programming is to write code that is independent of the data types. In C language, all codes are tied to a specific
data type. For container data structures (such as array and structure), you need to specify the type of the elements. For algorithms
(such as searching or sorting), the code works only for a specific type, you need to rewrite the code for another type. Can we write a
single sorting routine that works on all types (or most of the types) by specifying the type during invocation? Can we have a general
container that can work on all types?

Template lets you program on generic type, instead of on a specific type. Template supports so-called parameterized type - i.e., you
can use type as argument in building a class or a function (in class template or function template). Template is extremely useful if a
particular algorithm is to be applied to a variety of types, e.g., a container class which contains elements, possibly of various types.

C++'s Standard Template Library (STL) provides template implementation of many container classes, such as vector, which can be
used to hold elements of all types. STL also provides generic representation of algorithm (such as searching and sorting), which
works on the generic container.

2. Example: STL's vector Template Class


C/C++ built-in array has many drawbacks:
1. It is fixed-size and needs to be allocated with the fixed-size during declaration. It does not support dynamic allocation. You
cannot increase the size of an array during execution.
2. Array does not provide index-bound check. You could use an index which is outside the array's bound.
3. You need to roll your own codes to compare two arrays (via ==), copy an array into another array (via assignment =), etc.

C++ provides a vector template class, as part of Standard Template Library (STL). It is defined in header <vector>, belonging to
the namespace std. (In computing, a vector refers to an array-like structure that holds a set of direct-access elements of the same
kinds, instead of mathematical n-component vector.) vector is the most commonly used STL class, which provides an alternative to
the error-phone array, supports dynamic memory allocation and many operations (such as comparison and assignment).

vector is a template class, which can be instantiated with a type, in the format: vector<int>, vector<double>,
vector<string>. The same template class can be used to handle many types, instead of repeatably writing codes for each of the
type.

1 /* Test vector template class (TestVectorTemplate.cpp) */


2 #include <iostream>
3 #include <vector>
4 #include <string>
5 using namespace std;
6
7 void print(const vector<int> & v);
8
9 int main() {
10 vector<int> v1(5); // Create a vector with 5 elements;
11
12 // Assign values into v1, using array-like index []
13 // You can retrieve the size of vector via size()
14 for (int i = 0; i < v1.size(); ++i) {
15 v1[i] = (i+1) * 2; // no index-bound check for []
16 }
17
18 // Print vector content, using at()
19 for (int i = 0; i < v1.size(); ++i) {
20 cout << v1.at(i) << " "; // do index-bound check with at()
21 }
22 cout << endl;
23
24 vector<int> v2; // Create a vector with 0 elements
25 // Assign v1 to v2 memberwise
26 v2 = v1;
27 for (int i = 0; i < v2.size(); ++i) {
28 cout << v2[i] << " ";
29 }
30 cout << endl;
31
32 // Compare 2 vectors memberwise
33 cout << boolalpha << (v1 == v2) << endl;
34
35 // Append more elements - dynamically allocate memory
36 v1.push_back(80);
37 v1.push_back(81);
38 for (int i = 0; i < v1.size(); ++i) {
39 cout << v1[i] << " ";
40 }
41 cout << endl;
42
43 // Remove element from the end
44 v1.pop_back();
45 for (int i = 0; i < v1.size(); ++i) {
46 cout << v1[i] << " ";
47 }
48 cout << endl;
49
50 vector<string> v3; // Create a vector of string with 0 element
51 v3.push_back("A for Apple"); // append new elements
52 v3.push_back("B for Boy");
53 for (int i = 0; i < v3.size(); ++i) {
54 cout << v3[i] << " ";
55 }
56 cout << endl;
57 }

Program Notes:
You can instantiate a vector of int via declaration vector<int> v1(n), where n specifies the initial number of elements.
You can use v1.size() to get the current size (number of elements) of the vector.
You can directly access element via v1[i] or v1.at(i). The [] operator does not perform index-bound check; whereas at()
does. Try issuing an out-of-bound index via [] and at() and observe the result.
You can assign a vector into another vector via the assignment operator (=). The assignment is carried out memberwise.
You can compare two vectors memberwise via the comparison operators (==, !=).
You can append/remove element from the end via push_back() and pop_back(). The size of vector will be adjusted and
memory allocated automatically.
You can also create vector of other types, such as vector<string>, vector<double>, etc. The bigger advantage of template
class is the same algorithm and operation can be applied to different types - you do not need to write separate codes for different
types.

3. Function Template
A function template is a generic function that is defined on a generic type for which a specific type can be substituted. Compiler will
generate a function for each specific type used. Because types are used in the function parameters, they are also called
parameterized types.

The syntax of defining function template is:


template <typename T> OR template <class T>
return-type function-name(function-parameter-list) { ...... }

Similar to function's parameter-list, which passes variables (e.g., int number), the template's parameter list passes types.

Example 1
1 /* Test Function Template (FuncationTemplate.cpp) */
2 #include <iostream>
3 using namespace std;
4
5 template <typename T>
6 void mySwap(T &a, T &b);
7 // Swap two variables of generic type passed-by-reference
8 // There is a version of swap() in <iostream>
9
10 int main() {
11 int i1 = 1, i2 = 2;
12 mySwap(i1, i2); // Compiler generates mySwap(int &, int &)
13 cout << "i1 is " << i1 << ", i2 is " << i2 << endl;
14
15 char c1 = 'a', c2 = 'b';
16 mySwap(c1, c2); // Compiler generates mySwap(char &, char &)
17 cout << "c1 is " << c1 << ", c2 is " << c2 << endl;
18
19 double d1 = 1.1, d2 = 2.2;
20 mySwap(d1, d2); // Compiler generates mySwap(double &, double &)
21 cout << "d1 is " << d1 << ", d2 is " << d2 << endl;
22
23 // mySwap(i1, d1);
24 // error: no matching function for call to 'mySwap(int&, double&)'
25 // note: candidate is:
26 // note: template<class T> void mySwap(T&, T&)
27 }
28
29 template <typename T>
30 void mySwap(T &a, T &b) {
31 T temp;
32 temp = a;
33 a = b;
34 b = temp;
35 }

Take note that the C++ compiler generate a version of the code for each type used in the program, in a process known as
instantiation of template. For example, with type of int, the following code will be generated by the compiler:

void mySwap(int &a, int &b) {


int temp;
temp = a;
a = b;
b = temp;
}

There is no improvement in term of execution speed or memory usage. But there is a big improvement in programmer's productivity,
ease-of-use and ease-of-maintenance.

Also, the above code can handle the fundamental types (such as int, double), but not some types such as array. It, however, works
for objects and structs, thanks to the memberwise assignment.

You could use <typename T> or <class T> in the template parameter list. The keywords typename and class serve the same
purpose.

Example 2
1 /* Test function Template (TestFunctionTemplate.cpp) */
2 #include <iostream>
3 using namespace std;
4
5 template<typename T>
6 T abs(T value) {
7 T result; // result's type is also T
8 result = (value >= 0) ? value : -value;
9 return result;
10 }
11
12 int main() {
13 int i = -5;
14 cout << abs(i) << endl;
15
16 double d = -55.5;
17 cout << abs(d) << endl;
18
19 float f = -555.5f;
20 cout << abs(f) << endl;
21 }

The compiler creates three versions of the abs function based on the function template, for types of int, double, and float.

Function Template Overloading


1 /* Test Function Template Overloading (FuncationTemplateOverloading.cpp) */
2 #include <iostream>
3 using namespace std;
4
5 template <typename T>
6 void mySwap(T &a, T &b);
7 // Swap two variables of generic fundamental type
8
9 template <typename T>
10 void mySwap(T a[], T b[], int size);
11 // Swap two arrays of generic type
12
13 template <typename T>
14 void print(const T * const array, int size);
15 // Print an array of generic type
16
17 int main() {
18 int i1 = 1, i2 = 2;
19 mySwap(i1, i2); // Compiler generates mySwap(int &, int &)
20 cout << "i1 is " << i1 << ", i2 is " << i2 << endl;
21
22 const int SIZE = 3;
23 int ar1[] = {1, 2, 3}, ar2[] = {4, 5, 6};
24 mySwap(ar1, ar2, SIZE);
25 print(ar1, SIZE);
26 print(ar2, SIZE);
27 }
28
29 template <typename T>
30 void mySwap(T &a, T &b) {
31 T temp;
32 temp = a;
33 a = b;
34 b = temp;
35 }
36
37 template <typename T>
38 void mySwap(T a[], T b[], int size) {
39 T temp;
40 for (int i = 0; i < size; ++i) {
41 temp = a[i];
42 a[i] = b[i];
43 b[i] = temp;
44 }
45 }
46
47 template <typename T>
48 void print(const T * const array, int size) {
49 cout << "(";
50 for (int i = 0; i < size; ++i) {
51 cout << array[i];
52 if (i < size - 1) cout << ",";
53 }
54 cout << ")" << endl;
55 }
Explicit Specialization
1 /* Test Function Template Explicit Specialization (FuncationTemplateSpecialization.cpp) */
2 #include <iostream>
3 using namespace std;
4
5
6 template <typename T>
7 void mySwap(T &a, T &b); // Template
8
9 template <>
10 void mySwap<int>(int &a, int &b);
11 // Explicit Specialization for type int
12
13 int main() {
14 double d1 = 1, d2 = 2;
15 mySwap(d1, d2); // use template
16
17 int i1 = 1, i2 = 2;
18 mySwap(i1, i2); // use specialization
19 }
20
21 template <typename T>
22 void mySwap(T &a, T &b) {
23 cout << "Template" << endl;
24 T temp;
25 temp = a;
26 a = b;
27 b = temp;
28 }
29
30 template <>
31 void mySwap<int>(int &a, int &b) {
32 cout << "Specialization" << endl;
33 int temp;
34 temp = a;
35 a = b;
36 b = temp;
37 }
38

Take note that if there is any non-template definition that matches the function call. The non-template version will take precedence
over explicit specialization, then template.

4. Class Template
The syntax for defining a class template is as follow, where T is a placeholder for a type, to be provided by the user.

template <class T> // OR template <typename T>


class ClassName {
......
}

The keywords class and typename (newer and more appropriate) are synonymous in the definition of template.

To use the template defined, use the syntax ClassName<actual-type>.

For Example,

1 /*
2 * Test Class Template (TestClassTemplate.cpp)
3 */
4 #include <iostream>
5 using namespace std;
6
7 template <typename T>
8 class Number {
9 private:
10 T value;
11 public:
12 Number(T value) { this->value = value; };
13 T getValue() { return value; }
14 void setValue(T value) { this->value = value; };
15 };
16
17 int main() {
18 Number<int> i(55);
19 cout << i.getValue() << endl;
20
21 Number<double> d(55.66);
22 cout << d.getValue() << endl;
23
24 Number<char> c('a');
25 cout << c.getValue() << endl;
26
27 Number<string> s("Hello");
28 cout << s.getValue() << endl;
29 }

Separating Template Function Declaration and Definition


If you separate the function implementation, you need to use keyword template <typename T> on each of the function
implementation. For example,

template <typename T>


T Number<T>::getValue() {
return value;
}

Keep All Template Codes in the Header file


Templates are not class nor member function definition. They are instructions to the C++ compiler how to generate the class
definition. Hence, placing member functions in a separate file will not work. All template codes shall be placed in the header file - to
be included in all files using the template. Template cannot be compiled separately.

A particular realization of template is called an instantiation or specialization. The C++ compiler generate a class for each of the
parameterized type used in the program.

More than one Type Parameters


To use more than one type parameters in a template:

template <typename T1, typename T2, ....>


class ClassName { ...... }

Default Type
You can also provide default type in template. For example,

template <typename T = int>


class ClassName { ...... }

To instantiate with the default type, use ClassName<>.

Specialization
// General Template
template <typename T>
class Complex { ...... }

// Specialization for type double


template <>
class Complex<double> { ...... }

// Specialization for type int


template <>
class Complex<int> { ....... }

The specialization version will be matched, if available.

5. Example: MyComplex Template Class

MyComplex.h
1 /*
2 * The MyComplex template class header (MyComplex.h)
3 * All template codes are kept in the header, to be included in program
4 * (Follow, modified and simplified from GNU GCC complex template class.)
5 */
6 #ifndef MY_COMPLEX_H
7 #define MY_COMPLEX_H
8
9 #include <iostream>
10
11 // Forward declaration
12 template <typename T> class MyComplex;
13
14 template <typename T>
15 std::ostream & operator<< (std::ostream & out, const MyComplex<T> & c);
16 template <typename T>
17 std::istream & operator>> (std::istream & in, MyComplex<T> & c);
18
19 // MyComplex template class declaration
20 template <typename T>
21 class MyComplex {
22 private:
23 T real, imag;
24
25 public:
26 // Constructor
27 explicit MyComplex<T> (T real = 0, T imag = 0)
28 : real(real), imag(imag) { }
29
30 // Overload += operator for c1 += c2
31 MyComplex<T> & operator+= (const MyComplex<T> & rhs) {
32 real += rhs.real;
33 imag += rhs.imag;
34 return *this;
35 }
36
37 // Overload += operator for c1 += value
38 MyComplex<T> & operator+= (T value) {
39 real += value;
40 return *this;
41 }
42
43 // Overload comparison == operator for c1 == c2
44 bool operator== (const MyComplex<T> & rhs) const {
45 return (real == rhs.real && imag == rhs.imag);
46 }
47
48 // Overload comparison != operator for c1 != c2
49 bool operator!= (const MyComplex<T> & rhs) const {
50 return !(*this == rhs);
51 }
52
53 // Overload prefix increment operator ++c
54 // (Separate implementation for illustration)
55 MyComplex<T> & operator++ ();
56
57 // Overload postfix increment operator c++
58 const MyComplex<T> operator++ (int dummy);
59
60 /* friends */
61
62 // (Separate implementation for illustration)
63 friend std::ostream & operator<< <>(std::ostream & out, const MyComplex<T> & c); // out << c
64 friend std::istream & operator>> <>(std::istream & in, MyComplex<T> & c); // in >> c
65
66 // Overloading + operator for c1 + c2
67 // (inline implementation for illustration)
68 friend const MyComplex<T> operator+ (const MyComplex<T> & lhs, const MyComplex<T> & rhs) {
69 MyComplex<T> result(lhs);
70 result += rhs; // uses overload +=
71 return result;
72 }
73
74 // Overloading + operator for c + double
75 friend const MyComplex<T> operator+ (const MyComplex<T> & lhs, T value) {
76 MyComplex<T> result(lhs);
77 result += value; // uses overload +=
78 return result;
79 }
80
81 // Overloading + operator for double + c
82 friend const MyComplex<T> operator+ (T value, const MyComplex<T> & rhs) {
83 return rhs + value; // swap and use above function
84 }
85 };
86
87 // Overload prefix increment operator ++c
88 template <typename T>
89 MyComplex<T> & MyComplex<T>::operator++ () {
90 ++real; // increment real part only
91 return *this;
92 }
93
94 // Overload postfix increment operator c++
95 template <typename T>
96 const MyComplex<T> MyComplex<T>::operator++ (int dummy) {
97 MyComplex<T> saved(*this);
98 ++real; // increment real part only
99 return saved;
100 }
101
102 /* Definition of friend functions */
103
104 // Overload stream insertion operator out << c (friend)
105 template <typename T>
106 std::ostream & operator<< (std::ostream & out, const MyComplex<T> & c) {
107 out << '(' << c.real << ',' << c.imag << ')';
108 return out;
109 }
110
111 // Overload stream extraction operator in >> c (friend)
112 template <typename T>
113 std::istream & operator>> (std::istream & in, MyComplex<T> & c) {
114 T inReal, inImag;
115 char inChar;
116 bool validInput = false;
117 // Input shall be in the format "(real,imag)"
118 in >> inChar;
119 if (inChar == '(') {
120 in >> inReal >> inChar;
121 if (inChar == ',') {
122 in >> inImag >> inChar;
123 if (inChar == ')') {
124 c = MyComplex<T>(inReal, inImag);
125 validInput = true;
126 }
127 }
128 }
129 if (!validInput) in.setstate(std::ios_base::failbit);
130 return in;
131 }
132
133 #endif

Program Notes:
[TODO]

TestMyComplex.cpp
1 /* Test Driver for MyComplex template class (TestMyComplex.cpp) */
2 #include <iostream>
3 #include <iomanip>
4 #include "MyComplex.h"
5
6 int main() {
7 std::cout << std::fixed << std::setprecision(2);
8
9 MyComplex<double> c1(3.1, 4.2);
10 std::cout << c1 << std::endl; // (3.10,4.20)
11 MyComplex<double> c2(3.1);
12 std::cout << c2 << std::endl; // (3.10,0.00)
13
14 MyComplex<double> c3 = c1 + c2;
15 std::cout << c3 << std::endl; // (6.20,4.20)
16 c3 = c1 + 2.1;
17 std::cout << c3 << std::endl; // (5.20,4.20)
18 c3 = 2.2 + c1;
19 std::cout << c3 << std::endl; // (5.30,4.20)
20
21 c3 += c1;
22 std::cout << c3 << std::endl; // (8.40,8.40)
23 c3 += 2.3;
24 std::cout << c3 << std::endl; // (10.70,8.40)
25
26 std::cout << ++c3 << std::endl; // (11.70,8.40)
27 std::cout << c3++ << std::endl; // (11.70,8.40)
28 std::cout << c3 << std::endl; // (12.70,8.40)
29
30 // c1+c2 = c3; // error: c1+c2 returns a const
31 // c1++++; // error: c1++ returns a const
32
33 // MyComplex<int> c4 = 5; // error: implicit conversion disabled
34 MyComplex<int> c4 = (MyComplex<int>)5; // explicit type casting allowed
35 std::cout << c4 << std::endl; // (5,0)
36
37 MyComplex<int> c5;
38 std::cout << "Enter a complex number in (real,imag): ";
39 std::cin >> c5;
40 if (std::cin.good()) {
41 std::cout << c5 << std::endl;
42 } else {
43 std::cerr << "Invalid input" << std::endl;
44 }
45 return 0;
46 }

Program Notes:
[TODO]

Link to "C++ References & Resources"

Latest version tested: Cygwin/MinGW GCC 4.6.2


Last modified: May, 2013

Feedback, comments, corrections, and errata can be sent to Chua Hock-Chuan ([email protected]) | HOME

You might also like