Startertutorials Blog
Tutorials and articles related to programming, computer science, technology and others.
Subscribe to Startertutorials.com's YouTube channel for different tutorial and lecture videos.

Categories: C++ Programming. No Comments on Generic Programming in C++ Programming

This article provides a comprehensive overview of generic programming in C++ programming language along with example programs.

 

Introduction

To generate short, simple code and to avoid duplication of code, C++ provides templates to define the same piece of code for multiple data types. With templates, programmers can define a family of functions or classes that can perform operations on different types of data.

 

Templates comes under the category of meta-programming and auto code generation, where the generated code is not visible in general. Through templates, C++ supports generic programming.

 

Generic programming is a type of programming where the programmer specifies a general code first. That code is instantiated based on the type of parameters that are passed later in the program or at execution.

 

Entities such as a class or function created using generic programming are called generics.

 

Use of Templates

 

Following are the uses of templates in programming:

  • Templates are widely used to implement the Standard Template Library (STL).
  • Templates are used to create Abstract Data Types (ADTs) and classify algorithms and data structures.
  • Class templates are generally used to implement containers.

 

 

Function Templates

 

A function template is a function which contains generic code to operate on different types of data. This enables a programmer to write functions without having to specify the exact type of parameters. Syntax for defining a template function is as follows:

 

template<class Type, ...>
return-type function-name(Type arg1, ...)
{
	//Body of function template
}

 

As shown above, the syntax starts with the keyword template followed by a list of template type arguments or also called generic arguments.

 

The template keyword tells the compiler that what follows is a template. Here, class is a keyword and Type is the name of generic argument.

 

Following program demonstrates a template function or function template:

 

#include <iostream>
using namespace std;
template<class Type>
void summ(Type x, Type y)
{
	cout<<"Sum is: "<<x+y<<endl;
}
int main()
{
	int a = 10, b = 20;
	summ(a, b);
	float p = 1.5, q = 2.4;
	summ(p, q);
	return 0;
}

Output for the above program is as follows:

Sum is: 30
Sum is: 3.9

 

In the above program compiler generates two copies of the above function template. One for int type arguments and the other for float type arguments. The function template can be invoked implicitly by writing summ(val1, val2) or explicitly by writing summ<int>(val1, val2).

 

Templates vs. Macros

 

As templates and macros perform similar tasks, we need know what is the difference between them. Following are the differences between templates and macros:

 

template-vs-macros

 

Guidelines for Using Template Functions

 

While using template functions, programmer must take care of the following:

 

Generic Data Types

 

Every template data type (or generic data type) should be used as types in the function template definition as type of parameters. If not used, it will result in an error. Consider the following example which leads to an error:

 

template<class Type>
void summ(int x, int y)	//Error since Type is not used for arguments x and y
{
	cout<<"Sum is: "<<x+y<<endl;
}

 

Also using some of the template data types is also wrong. Consider the following example which leads to an error:

 

template<class Type1, class Type2>
void summ(Type1 x, int y)	//Error since Type2 is not used
{
	cout<<"Sum is: "<<x+y<<endl;
}

 

Overloading Function Templates

 

As normal functions can be overloaded, template functions can also be overloaded. They will have the same name but different number of or type of parameters. Consider the following example which demonstrates template function overloading:

 

#include <iostream>
using namespace std;
void summ(int x, int y)
{
	cout<<"Normal Function: "<<endl;
	cout<<"Sum is: "<<x+y<<endl;
}
template<class Type1, class Type2>
void summ(Type1 x, Type2 y)
{
	cout<<"Template Function: "<<endl;
	cout<<"Sum is: "<<x+y<<endl;
}
int main()
{
	int a = 10, b = 20;
	summ(a, b);
	float p = 1.5, q = 2.4;
	summ(p, q);
	return 0;
}

Output for the above program is as follows:

Normal Function:
Sum is: 30
Template Function:
Sum is: 3.9

 

Whenever a compiler encounters the call to a overloaded function, first it checks if there is any normal function which matches and invokes it. Otherwise, it checks if there is any template function which matches and invokes it. If no function matches, error will be generated.

 

From the above example you can see that for the call summ(a, b), normal function is invoked and for the call summ(p, q), template function is invoked.

 

Recursive Template Functions

 

Like normal functions, template functions can also be called recursively. Consider the following example which demonstrates a recursive template function that calculates the factorial of a number:

 

#include <iostream>
using namespace std;
template<class Type>
Type fact(Type n)
{
	if(n==0 || n==1)
		return 1;
	else
		return n*fact(n-1);
}
int main()
{
	int n = 5;
	cout<<"Factorial of 5 is: "<<fact(5);
	return 0;
}

Output of the above program is as follows:

Factorial of 5 is: 120

 

Function Templates with User-defined Types

 

We can also pass user-defined types like class, structure, union as arguments to a function template. Consider the following function template which demonstrates passing a class as an argument:

 

#include <iostream>
using namespace std;
class Student
{
	public:
		int age;
		string name;
		Student(int age, string name)
		{
			this->age = age;
			this->name = name;
		}
};
template<class Type>
void display(Type &obj)
{
	cout<<"Name is: "<<obj.name<<endl;
	cout<<"Age is: "<<obj.age<<endl;
}
int main()
{
	Student s(25, "suresh");
	display(s);
	return 0;
}

Output for the above program is as follows:

Name is: suresh
Age is: 25

 

Class Template

 

Using templates programmers can create abstract classes that define the behavior of the class without actually knowing what data type will be handled by the class operations. Such classes are known as class templates. Syntax for creating a class template is as follows:

 

template<class Type1, class Type2, ...>
class ClassName
{
	...
	//Body of the class
	...
};

 

Syntax for creating an object of the template class is as follows:

 

ClassName<Type> ObjectName(params-list);

 

The process of creating an object of a template class or creating a specific class from a class template is called instantiation. The instantiated object of a template class is known as specialization. If a class template is specialized by some but not all parameters it is called partial specialization and if all the parameters are specialized, it is a full specialization.

 

Following program demonstrates creating a class template and using it:

 

#include <iostream>
using namespace std;
template<class Type>
class Swapper
{
	public:
		Type x, y;
	public:
		Swapper(Type x, Type y)
		{
			this->x = x;
			this->y = y;
		}
		void swap()
		{
			Type temp;
			temp = x;
			x = y;
			y = temp;
		}
		void display()
		{
			cout<<"x="<<x<<", y="<<y<<endl;
		}
};
int main()
{
	Swapper<int> iobj(10, 20);
	cout<<"Before swap:"<<endl;
	iobj.display();
	iobj.swap();
	cout<<"After swap:"<<endl;
	iobj.display();
	Swapper<float> fobj(10.3, 20.6);
	cout<<"Before swap:"<<endl;
	fobj.display();
	fobj.swap();
	cout<<"After swap:"<<endl;
	fobj.display();
	return 0;
}

Output for the above program is as follows:

Before swap:
x=10, y=20
After swap:
x=20, y=10
Before swap:
x=10.3, y=20.6
After swap:
x=20.6, y=10.3

 

The template data types allows default arguments. Syntax for specifying default data types is as follows:

 

template<class Type1, class Type2 = int>
class ClassName
{
	...
	//Body of the class
	...
};

 

We can define member functions of a class template outside the class. Syntax for doing it is as follows:

 

template<class Type>
return-type ClassName<Type> :: function-name(params-list)
{
	...
	//Body of function
	...
}

 

Class Template and Friend Functions

 

A friend function is a normal (non-member) function which can access the private members of a class. A function can be declared as a friend function inside the template class using the following syntax:

 

template<class Type>
class ClassName
{
	private:
		...
	public:
		...
		template<class Type1>
		friend return-type function-name(ClassName<Type1> Obj);
};

 

Following example demonstrates a friend function in class template:

 

#include <iostream>
using namespace std;
template<class Type>
class Swapper
{
	public:
		Type x, y;
	public:
		Swapper(Type x, Type y)
		{
			this->x = x;
			this->y = y;
		}
		template<class Type1>
		friend void swap(Swapper<Type1> &);
		void display()
		{
			cout<<"x="<<x<<", y="<<y<<endl;
		}
};
template<class Type1>
void swap(Swapper<Type1> &s)
{
	Type1 temp;
	temp = s.x;
	s.x = s.y;
	s.y = temp;
}
int main()
{
	Swapper<int> iobj(10, 20);
	cout<<"Before swap:"<<endl;
	iobj.display();
	swap(iobj);
	cout<<"After swap:"<<endl;
	iobj.display();
	Swapper<float> fobj(10.3, 20.6);
	cout<<"Before swap:"<<endl;
	fobj.display();
	swap(fobj);
	cout<<"After swap:"<<endl;
	fobj.display();
	return 0;
}

Output of the above program is as follows:

Before swap:
x=10, y=20
After swap:
x=20, y=10
Before swap:
x=10.3, y=20.6
After swap:
x=20.6, y=10.3

 

Class Templates and Static Variables

 

We know that static variables are shared by all the objects of a class. In class templates, all the objects of same type share the same static variable. Following program demonstrates a static variable inside a class template:

 

#include <iostream>
using namespace std;
template<class Type>
class Sample
{
	private:
		Type data;
	public:
		static int count;
		Sample()
		{
			count++;
		}
		static void show()
		{
			cout<<count<<endl;
		}
};
template<class Type>
int Sample<Type> :: count = 0;
int main()
{
	Sample<int> i1;
	Sample<int> i2;
	Sample<int> i3;
	Sample<char> c;
	Sample<double> d1;
	Sample<double> d2;
	cout<<"Number of integer objects: ";
	Sample<int>::show();
	cout<<"Number of char objects: ";
	Sample<char>::show();
	cout<<"Number of double objects: ";
	Sample<double>::show();
	return 0;
}

Output of the above program is as follows:

Number of integer objects: 3
Number of char objects: 1
Number of double objects: 2

 

Class Templates and Inheritance

 

A template class can be used in inheritance in the following ways:

  • Deriving a class template from base class.
  • Deriving a class template from a base class which also a class template and adding more template members to it.
  • Deriving a class template from a base class which is a template class but disallowing the derived class and its derivatives to have template features.
  • Deriving a non-template base class and adding some template members to it.

 

The syntax for declaring a derived class from base class which is a template class is as follows:

 

template<class Type1, class Type2>
class Base
{
	...
	//Body of class
	...
};
template<class Type1, class Type2>
class Derived : public Base<Type1, Type2>
{
	...
	//Body of class
	...
};

 

The derived class can be either a template class or a non-template class. If the derived class is a template class, then type of template arguments must be specified while creating its object. If the derived class is a non-template class, then type of template arguments must be specified during derivation itself as follows:

 

class Derived : public Base<int, float>
{
	...
	//Body of class
	...
};

 

Following example demonstrates creating a derived class which is a template class from a base class which also a template class:

 

#include <iostream>
using namespace std;
template<class Type1>
class Base
{
	protected:
		Type1 x;
	public:
		Base(Type1 x)
		{
			this->x = x;
		}
		void display()
		{
			cout<<"x = "<<x<<endl;
		}
};
template<class Type1, class Type2>
class Derived : public Base<Type1>
{
	private:
		Type2 y;
	public:
		Derived(Type1 x, Type2 y) : Base<Type1>(x)
		{
			this->y = y;
		}
		void display()
		{
			Base<Type1>::display();
			cout<<"y = "<<y<<endl;
		}
};
int main()
{
	Base<int> bobj(10);
	bobj.display();
	Derived<int,int> dobj(10, 20);
	dobj.display();
	return 0;
}

Output of the above program is as follows:

x = 10
x = 10
y = 20

 

Advantages and Disadvantages of Templates

 

The advantages of templates are as follows:

  • Templates not only increases reusability of code but also makes the code short and simple.
  • A single template can handle different types of parameters.
  • Testing and debugging becomes simple.
  • Templates support on-demand compilation i.e., only necessary template instances are compiled. In a program without templates all classes are compiled irrespective of the needed ones.

 

The disadvantages of templates are as follows:

  • Some compilers doesn’t support all template features. This reduces portability.
  • Time needed to debug the code and resolve a error by the compiler needs more time.
  • As templates are declared in the header, code is visible to all programs. This is in contradiction to information hiding.

 

Bubble Sort using Function Template

 

Following program performs Bubble sort using function templates:

 

#include <iostream>
using namespace std;
template<class Type1, class Type2>
void sort(Type1 arr[], Type2 n)
{
	for(int i = 0; i < n - 1; i++)
	{
		for(int j = 0; j < n - i - 1; j++)
		{
			if(arr[j] > arr[j+1])
			{
				Type1 temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
			}
		}
	}
}
int main()
{
	int n;
	cout<<"Enter the no. of elements: ";
	cin>>n;
	cout<<"Enter "<<n<<" integers: ";
	int iarr[n];
	for(int i=0; i<n; i++)
	{
		cin>>iarr[i];
	}
	cout<<"Enter "<<n<<" float values: ";
	float farr[n];
	for(int i=0; i<n; i++)
	{
		cin>>farr[i];
	}
	sort(iarr, n);
	sort(farr, n);
	cout<<"After sorting integer values are: ";
	for(int i=0; i<n; i++)
	{
		cout<<iarr[i]<<" ";
	}
	cout<<"\nAfter sorting floating point values are: ";
	for(int i=0; i<n; i++)
	{
		cout<<farr[i]<<" ";
	}
	return 0;
}

Input and Output for the above program are as follows:

Enter the no. of elements: 5
Enter 5 integers: 3 2 1 6 4
Enter 5 float values: 8.8 3.3 2.2 1.1 5.5
After sorting integer values are: 1 2 3 4 6
After sorting floating point values are: 1.1 2.2 3.3 5.5 8.8

 

Linked List using Class Template

 

Following program demonstrates a linked list using class template:

 

#include <iostream>
using namespace std;
template<class T>
class List
{
	public:
		T data;
		List *next;
		List* makenode(T data)
		{
			List *t = new List;
			t->data = data;
			t->next = NULL;
			return t;
		}
		//Print the linked list items
		void printlist(List *head)
		{
			while(head != NULL)
			{
				cout<<head->data<<" ";
				head = head->next;
			}
			cout<<endl;
		}
		//Insert a node at head of the list
		List* inshead(List *head, int data)
		{
			List *t = new List;
			t->data = data;
			t->next = head;
			head = t;
			return head;
		}
		//Insert a node at tail of the list
		List* instail(List *tail, int data)
		{
			List *t = new List;
			t->data = data;
			t->next = NULL;
			tail->next = t;
			tail = t;
			return tail;
		}
		//Delete a node at head of the list
		List* delhead(List *head)
		{
			head = head->next;
			return head;
		}
		//Delete a node at tail of the list
		List* deltail(List *head, List *tail)
		{
			List *prev, *curr;
			curr = head;
			while(curr->next != NULL)
			{
				prev = curr;
				curr = curr->next;
			}
			prev->next = NULL;
			tail = prev;
			return tail;
		}
};
int main()
{
	List<int> *head = NULL, *tail = NULL;
	List<int> l;
	int n;
	cout<<"How many numbers you want to store? ";
	cin>>n;
	for(int i=1; i<=n; i++)
	{
		int data;
		cout<<"Enter number "<<i<<": ";
		cin>>data;
		List<int> *temp = l.makenode(data);
		if(head == NULL)
		{
			head = temp;
		}
		else
		{
			tail->next = temp;
		}
		tail = temp;
	}
	cout<<"Items in the list: ";
	l.printlist(head);
	cout<<"After inserting 10 at head: ";
	head = l.inshead(head, 10);
	l.printlist(head);
	cout<<"After deleting the head node: ";
	head = l.delhead(head);
	l.printlist(head);
	cout<<"After inserting 20 at tail: ";
	tail = l.instail(tail, 20);
	l.printlist(head);
	cout<<"After deleting the tail node: ";
	tail = l.deltail(head, tail);
	l.printlist(head);
	return 0;
}

 

Input and output for the above program are as follows:

How many numbers you want to store? 5
Enter number 1: 2
Enter number 2: 1
Enter number 3: 6
Enter number 4: 4
Enter number 5: 7
Items in the list: 2 1 6 4 7
After inserting 10 at head: 10 2 1 6 4 7
After deleting the head node: 2 1 6 4 7
After inserting 20 at tail: 2 1 6 4 7 20
After deleting the tail node: 2 1 6 4 7

 

How useful was this post?

Click on a star to rate it!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Suryateja Pericherla

Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.

He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.

He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.

Leave a Reply

Your email address will not be published. Required fields are marked *