C++ generic programming introduction
C++ generic programming Introduction
Writing code that may be used with various data types while upholding a high level of code reuse and type safety is known as generic programming in C++. Templates, which let you design functions or classes that can operate on a variety of data types without explicitly mentioning them, are the foundational idea of generic programming.
C++ template usage techniques
There are several ways to use templates in C++ to implement generic programming. Listed below are a few typical applications for templates:
1.Function Template
Function templates serve a purpose in C++ generic programming. Interfaces encourage code reuse and type safety through enabling you to establish generic functions that may be employed with many data types. The template keyword is used to define function templates, then the template for the parameters collection and the function signature occurs next.
A function template can be defined using the general syntax shown here:
template <typename T>
return_type function_name(parameters) {
// Function body
}
Let's begin by examine an illustration of a function template for determining the highest value between two ethics:
Program:
#include <iostream>
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
int num1 = 10, num2 = 20;
double num3 = 3.14, num4 = 2.71;
std::cout << "Max of " << num1 << " and " << num2 << " is " << max(num1, num2) << std::endl;
std::cout << "Max of " << num3 << " and " << num4 << " is " << max(num3, num4) << std::endl;
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction1.png)
Explanation:
In this case, the function template max returns the highest value possible between two parameters of type T. Given that the comparison operators (>,, etc.) are specified for the data type, the function is generic and can be used with a variety of data types, including int, double, and others.
The compiler creates a unique instance of the max function for the data type used in the function call whenever the max function is called.
2.Class Template
Another crucial component of generic programming in C++ is class templates. They grant you an opportunity for reusing functionality throughout broad structures of information whereas conserving type confidentiality through enabling customers to build generalist subclasses the fact that are able to operate on various types of information.
A class template can be defined using syntax that is similar to that of a function template:
template <typename T>
class ClassName {
public:
// Member functions and data members using the type T
private:
// Private data members using the type T
};
Listed below is an illustration of a MyContainer class template:
Program:
#include <iostream>
template <typename T>
class MyContainer {
public:
MyContainer(T value) : data(value) {}
T getData() const {
return data;
}
private:
T data;
};
int main() {
MyContainer<int> container1(42);
MyContainer<double> container2(3.14);
std::cout << "Data in container1: " << container1.getData() << std::endl;
std::cout << "Data in container2: " << container2.getData() << std::endl;
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction2.png)
Explanation:
The class template MyContainer in this illustration has just one template parameter, T. Its constructor initialises a T-type private member data. The value of the data is returned by the member function getData.
We make two instances of the MyContainer class in the main method, one with an int template argument and the other with a double template argument. For each of the used data types, the compiler creates a different version of the class.
When you want to design a data structure that can hold many data kinds without duplicating code, class templates can be helpful. You must be mindful of potential specialisation cases, much as with function templates, and make sure that all the data types supported by the class' operations are supported. Containers from the standard library like std::vector, std::list, and std::map frequently employ class templates.
3. Template Functions and Classes with Multiple Template Parameters
With many template parameters, you can define template functions and classes in C++. This enables you to develop generic code that is more adaptable and flexible, operates on many types simultaneously, or accepts more non-type parameters. You can utilise several template parameters in the following ways:
- Template Functions with Multiple Template Parameters:
Program:
#include <iostream>
template <typename T, typename U>
T addAndMultiply(T a, U b, int factor) {
return a + b * static_cast<T>(factor);
}
int main() {
int result1 = addAndMultiply(5, 10, 2);
double result2 = addAndMultiply(3.14, 2.71, 3);
std::cout << "Result 1: " << result1 << std::endl;
std::cout << "Result 2: " << result2 << std::endl;
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction3.png)
Explanation:
With distinct data types (int and double) and a different factor value, the addAndMultiply function is called in this example. The function's expected output is shown using std::cout, and it returns the outcome of adding a to the product of b and factor.
2.Template Classes with Multiple Template Parameters:
Program:
#include <iostream>
template <typename T, int Size>
class FixedArray {
public:
FixedArray() : size(Size) {
data = new T[Size];
}
T& operator[](int index) {
return data[index];
}
int getSize() const {
return size;
}
private:
T* data;
int size;
};
int main() {
FixedArray<int, 5> arr1;
FixedArray<double, 10> arr2;
arr1[0] = 42;
arr2[3] = 3.14;
std::cout << "Size of arr1: " << arr1.getSize() << std::endl;
std::cout << "Size of arr2: " << arr2.getSize() << std::endl;
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction4.png)
Explanation:
Using this illustration, the definition of the template class FixedArray comprises a pair blueprint parameter: T and Size. The category embodies a fixed-size Dimensions collection that contains T-type constituent pieces. At compile time, the size is determined.
3.Mix of Type and Non-Type Template Parameters:
In C++, you may design template functions and classes that are even more adaptable and versatile by combining type and non-type template parameters. In the same template definition, type parameters (used to indicate types) and non-type parameters (used to specify constant values) can both be used. As an illustration, consider the following use of type and non-type template parameters:
Program:
#include <iostream>
template <typename T, int Size>
void printArray(const T (&arr)[Size]) {
for (int i = 0; i < Size; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int intArray[] = {1, 2, 3, 4, 5};
double doubleArray[] = {3.14, 2.71, 1.618};
printArray(intArray);
printArray(doubleArray);
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction5.png)
Explanation:
The printArray function in this example is a template function with two parameters: Size, a non-type parameter, and T, a type parameter. The non-type parameter Size specifies the fixed size of the array of type T that the function accepts as a reference. The array's whole contents are then printed.
Different array types can be used to call the printArray function, and the function is going to calculate the array size based on the template given.
4.Template Specialization
With C++'s sophisticated template specialisation feature, you can create unique implementations of templates for particular types or use cases. It enables you to precisely control how a general template behaves in relation to certain circumstances or data types. When the general implementation is inadequate for a particular type or when you need to handle particular circumstances differently, template specialisation is utilised.
Two distinct categories of template specialisation exist:
Function Template Specialisation: By offering a unique implementation for a certain data type, you can specialise a function template for that data type. Here is an illustration of a function template for computing a number's square, along with a variant specifically for std::string:
Program:
#include <iostream>
#include <string>
template <typename T>
T square(T value) {
return value * value;
}
template <>
std::string square(std::string value) {
return value + value;
}
int main() {
int num = 5;
double num2 = 3.14;
std::string str = "Hello";
std::cout << "Square of " << num << " is " << square(num) << std::endl;
std::cout << "Square of " << num2 << " is " << square(num2) << std::endl;
std::cout << "Square of \"" << str << "\" is \"" << square(str) << "\"" << std::endl;
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction6.png)
Explanation:
In this illustration, a unique implementation of the square function template is provided for use with std::string. The specialised version concatenates the string with itself, whereas the generic version works for numeric types.
Class Template Specialisation: By supplying a unique definition for that specialisation, you can specialise a class template for a particular data type or configuration. Here is an illustration of the MyContainer class template and a variant specifically designed for char*:
Program:
#include <iostream>
#include <cstring>
template <typename T>
class MyContainer {
public:
MyContainer(T value) : data(value) {}
void print() {
std::cout << data << std::endl;
}
private:
T data;
};
template <>
class MyContainer<char*> {
public:
MyContainer(char* value) : data(std::strcpy(new char[std::strlen(value) + 1], value)) {}
void print() {
std::cout << "Specialized: " << data << std::endl;
}
private:
char* data;
};
int main() {
MyContainer<int> container1(42);
MyContainer<double> container2(3.14);
MyContainer<char*> container3("Hello");
container1.print();
container2.print();
container3.print();
return 0;
}
Output:
![C++ generic programming introduction](https://static.tutorialandexample.com/cpp/cpp-generic-programming-introduction7.png)
Explanation:
For the purpose of managing dynamic memory allocation for strings in this example, the MyContainer class template is specialised for char*.
Although template specialisation is a potent technique, care should be used when using it because it can make maintaining the code more difficult. It is particularly helpful when handling particular situations that considerably depart from the general behaviour or when attempting to maximise performance for certain data types.
Benefits of C++ generic programming include:
- Code reuse: Allows for the creation of code that functions with several data types, hence minimising repetition.
- Flexibility: Develops flexible algorithms and data structures that can be adapted to many types.
- Type Safety: Thorough type checking during compilation increases code robustness and enables early error detection.
- Performance: Can result in the efficient creation of machine code using specialised code.
Generic programming in C++ has drawbacks.
- Code bloat: The size of the compiled binary can rise as a result of several template instantiations.
- Compilation Time: In particular for large projects, templates may result in lengthier compilation times.
- Readability: Complex templates might make it tougher to read the code, which makes maintenance and troubleshooting more difficult.
- Compiler error signals from template-related issues might be complex and challenging to decipher.
- Use templates sparingly and strike a balance between code reuse and complexity to maximise the advantages of generic programming.