C++ Generic Programming Introduction
By employing C++ templates, the concept of generic programming is extended to accommodate different data types. Instead of explicitly defining the data type, templates introduce a placeholder that gets replaced with the specific data type during compilation. Consequently, if the template function is invoked with an integer, character, and float, the compiler would produce three distinct versions of the function. This capability is made possible due to C++'s utilization of static types.
Ways to use a Template
Let's take a quick look at conventional programming to grasp the significance of templates. In C++ programming, it is essential to store all information in a container known as data types, such as int, float, custom-defined, and so forth.
The method of computing the distance between two sets of coordinates remains consistent, irrespective of whether the coordinates are given as whole numbers or decimal numbers, since the methodology can be applied universally across various data formats. Integer data types are unable to accommodate floating-point values, and employing floating-point data types for integers results in inefficient memory usage. Consequently, in C++ development, distinct functions must be created for handling each data type. Thus, templates in C++ provide a versatile resolution to this issue.
Overview
The code that is not specific to any data type in C++ is implemented using templates. During compilation, the code undergoes instantiation where the placeholder in the code is replaced with the actual data type. Generics pertain to the class or function declared as a template, whereas in C++, "generic programming" encompasses the entire concept.
Function templates are essential for creating functions that can adapt their behavior based on the data type specified when calling the function. This feature eliminates the need for redundant code and allows for consistent actions across different data types.
Function templates are utilized to create functions with generic types that can adapt their functionality based on the data type specified when the function is called. This approach streamlines the process of performing identical operations across various data types, eliminating the need for redundant code.
# include < iostream >
// Template Function with a Type T
// This T will be changed to the data type of the argument during instantiation.
template < class T >
T maxNum ( T x , T y ) {
return ( a > y ? a : y ) ;
}
int main ( )
{
int a = 5 , b = 2 ;
float i = 4.5 , j = 1.3 ;
std :: cout < < maxNum < int > ( a , b ) < < " \ n " ;
std :: cout < < maxNum < float > ( c , d ) ;
return 0 ;
}
OUTPUT:
5
4.5
???..
Process executed in 0.11 seconds
Press any key to continue.
Explanation
The function call in the example provided included the data type, but it may not always be necessary as the compiler can infer it from the values passed to the function.
Generic Data Type
Classes or functions that offer flexibility with types are referred to as generic types. This flexibility is achieved in C++ through templates. Templates allow us to create generic data types by specifying template parameters. When we provide template arguments, the function receives them as type values, resembling the concept of function parameters.
Below is syntax:
template < class id > function_decl ;
// or
template < typename id > function_decl ;
Either the typename or the class keyword can be utilized, and both approaches are considered interchangeable.
The same file Must Contain Both the Declaration and the Definition
There are no standard functions present within templates. They are exclusively compiled when instantiated with specific template inputs. The compiler produces exact functionality based on the provided arguments and template. Due to the on-demand compilation nature of templates, it is not feasible to split the template functions' definition and declaration into separate files.
Overloading Function Templates
Template functions can be overloaded, enabling the compiler to locate the specific definition of the overloaded function when called within the program. If the overloaded function is found, it will be executed; otherwise, the corresponding template function will be executed instead. Furthermore, an error will be generated by the compiler if a function call lacks a template function.
# include < iostream >
using namespace std ;
template < class T >
void sumoftwonum ( T x , T y ) {
cout < < " Inside Template " < < x + y < < endl ;
}
Void sumoftwonum ( int x , int y ) {
cout < < " Inside Overload " < < x + y < < endl ;
}
int main ( )
{
// Template Function will be called.
sum ( 2.1 , 3.8 ) ;
// Overload will be called.
sum ( 5 , 5 ) ;
return 0 ;
}
OUTPUT:
Inside Template 5.9
Inside Overload 10
???????????..
Process executed in 0.11 seconds
Press any key to continue.
Calling the sum(2.1, 3.8) function triggers the compiler to search for the specific function, which is not available, and then searches for the template function, which is present and can be executed.
In contrast, when the sum(5, 5) function is called with the exact data type, an overload function specific to that data type is used, bypassing the template function.
Template Recursive Functions
Recursion can be achieved through template functions, and in terms of program execution, it operates similarly to a standard recursive function.
Templates for Functions with User-Defined Types
Any kind of data that we specify within the function template's template parameters is fully under our control. This means that we have the freedom to utilize user-defined types in order to define function templates.
# include < iostream >
using namespace std ;
class Base
{
private :
int ageofp ;
string nameofp ;
public :
Person ( string _name_person , int _age )
{
ageofp = _age ;
nameofp = _name_person ;
}
void toString ( )
{
cout < < name < < " is " < < ageofp < < " years old. " < < endl ;
}
} ;
template < class T >
void printDataofbase ( T & obj )
{
obj.toString ( ) ;
}
int main ( )
{
Base b1 = Base ( " Vikram Sharma " , 21 ) ;
printDataofbase ( b1 ) ;
return 0 ;
}
OUTPUT:
Vikram Sharma is 21 years old.
???????????..
Process executed in 0.11 seconds
Press any key to continue.
Explanation:
- Base is a user-defined type that we have built. It has a function toString { [native code] } function, a function Object { [native code] }, and a few private entities.
- There is then a function template that can be used to print the data by calling it with the appropriate user-defined data type.
- The copy of the template function with the specific data type will be created when the compiler parses the call printDataofbase(b1), and this call will be calling that copy of the template function where Person replaces T.
Class Templates
The class can also utilize templates, similar to function templates, in order to ensure compatibility with various data types. For instance, when you create a dynamic array using the vector in C++ programming, you will observe its seamless operation with any data type specified within the <>, like vector < int >. The responsibility for this smooth functionality lies solely with the class template.
template < class T >
class className {
// Class Definition.
// We can use T as a type inside this class.
};
Class Template and Friend Function
A companion function is an external function that is not a part of the class but can access the private and protected members of the class. These functions are utilized to establish connections between classes and functions. Within the template of a class, we have the option to designate our companion function as either a template or a standard function.
Class Template and Static Function
In C++, classes have the option to incorporate either static or non-static variables (instance variables). Each object of a class holds its own set of non-static variables. However, the static variable remains the same across all objects, being shared among all instances created. It's important to note that in C++ templates, the static variable within template classes is also shared among all instances of that specific type.