In C++, generics (using templates) allow you to write functions and classes that work with any data type.
This powerful feature enables you to create generic algorithms and data structures that can operate with different types, reducing code duplication and improving flexibility.
Basic Types of Templates in C++
- Function Templates: Write generic functions that can work with multiple data types.
- Class Templates: Define classes that can handle various data types.
- Template Specialization: Provide specific implementations for particular data types.
- Non-type Template Parameters: Use values as template parameters.
1. Basic Function Template
A function template allows you to write a single function that can work with different data types.
#include <iostream> using namespace std; template <typename T> T add(T a, T b) { return a + b; } int main() { cout << "Integer addition: " << add(5, 3) << endl; cout << "Double addition: " << add(2.5, 3.1) << endl; return 0; }
Explanation:
- template <typename T> defines a template where T represents a generic type.
- add is a template function that adds two values of type T.
- The function automatically determines T based on the argument types provided.
Output:
Integer addition: 8 Double addition: 5.6
2. Function Template with Multiple Parameters
You can define multiple template parameters for functions.
#include <iostream> using namespace std; template <typename T1, typename T2> void display(T1 a, T2 b) { cout << "a: " << a << ", b: " << b << endl; } int main() { display(5, 2.5); // Different data types display("Hello", 'A'); // String and char return 0; }
Explanation:
- template <typename T1, typename T2> defines two template parameters, T1 and T2.
- display can accept arguments of different types for a and b.
Output:
a: 5, b: 2.5 a: Hello, b: A
3. Basic Class Template
Class templates allow you to define a generic class that can handle different data types.
#include <iostream> using namespace std; template <typename T> class Box { private: T value; public: Box(T v) : value(v) {} T getValue() { return value; } }; int main() { Box<int> intBox(100); Box<double> doubleBox(3.14); cout << "intBox value: " << intBox.getValue() << endl; cout << "doubleBox value: " << doubleBox.getValue() << endl; return 0; }
Explanation:
- template <typename T> defines a class template with T as a generic type.
- Box<T> stores a value of type T and provides a method to access it.
- Box<int> and Box<double> create Box objects for int and double types, respectively.
Output:
intBox value: 100 doubleBox value: 3.14
4. Class Template with Multiple Parameters
You can use multiple template parameters in a class.
#include <iostream> using namespace std; template <typename T1, typename T2> class Pair { private: T1 first; T2 second; public: Pair(T1 a, T2 b) : first(a), second(b) {} void display() { cout << "First: " << first << ", Second: " << second << endl; } }; int main() { Pair<int, double> p1(10, 3.14); Pair<string, char> p2("Hello", 'A'); p1.display(); p2.display(); return 0; }
Explanation:
- template <typename T1, typename T2> defines a template with two types, T1 and T2.
- Pair stores two values of different types and displays them using the display method.
Output:
First: 10, Second: 3.14 First: Hello, Second: A
5. Template Specialization
Template specialization allows you to provide a specific implementation for a particular type.
#include <iostream> using namespace std; template <typename T> class Printer { public: void print(T value) { cout << "Value: " << value << endl; } }; // Specialization for `char*` type template <> class Printer<char*> { public: void print(char* value) { cout << "String: " << value << endl; } }; int main() { Printer<int> intPrinter; Printer<char*> stringPrinter; intPrinter.print(100); stringPrinter.print("Hello"); return 0; }
Explanation:
- Printer is a generic class with a print function.
- A specialized version for char* provides custom behavior to print strings.
- Printer<int> uses the generic version, while Printer<char*> uses the specialized version.
Output:
Value: 100 String: Hello
6. Function Template Specialization
Function templates can also be specialized for specific types.
#include <iostream> using namespace std; template <typename T> void printValue(T value) { cout << "Generic Value: " << value << endl; } // Specialized version for `double` template <> void printValue(double value) { cout << "Double Value: " << value << endl; } int main() { printValue(5); printValue(3.14); return 0; }
Explanation:
- printValue is a generic function template.
- A specialized version is provided for double type, which has a different output format.
Output:
Generic Value: 5 Double Value: 3.14
7. Non-type Template Parameters
Templates can also accept values (non-type parameters), such as integers or pointers, instead of types.
#include <iostream> using namespace std; template <int N> void printStars() { for (int i = 0; i < N; i++) { cout << "*"; } cout << endl; } int main() { printStars<5>(); printStars<10>(); return 0; }
Explanation:
- template <int N> defines a template with an integer parameter N.
- printStars<N> prints N asterisks, with the number of stars specified at compile time.
Output:
***** **********
8. Template with Default Type Parameters
Templates can have default types for parameters, allowing for optional specification.
#include <iostream> using namespace std; template <typename T = int> class Number { public: T value; Number(T v) : value(v) {} void display() { cout << "Value: " << value << endl; } }; int main() { Number<> num1(10); // Uses default type int Number<double> num2(3.14); // Specifies double type num1.display(); num2.display(); return 0; }
Explanation:
- template <typename T = int> sets int as the default type for T.
- Number<> uses the default int type, while Number<double> explicitly specifies double.
Output:
Value: 10 Value: 3.14
9. Template Functions with auto Return Type
The auto keyword can infer the return type of a template function.
#include <iostream> using namespace std; template <typename T1, typename T2> auto multiply(T1 a, T2 b) -> decltype(a * b) { return a * b; } int main() { cout << "Multiply int and double: " << multiply(5, 3.14) << endl; cout << "Multiply int and int: " << multiply(4, 2) << endl; return 0; }
Explanation:
- auto and decltype deduce the return type based on the types of a and b.
- multiply can handle different data types for a and b, calculating the correct result type.
Output:
Multiply int and double: 15.7 Multiply int and int: 8
10. Template Aliases
Template aliases simplify complex type names by creating type shortcuts.
#include <iostream> #include <vector> using namespace std; template <typename T> using Vec = vector<T>; int main() { Vec<int> numbers = {1, 2, 3, 4, 5}; // Vec<int> is equivalent to vector<int> cout << "Elements in Vec<int>: "; for (int n : numbers) { cout << n << " "; } return 0; }
Explanation:
- `template using
Vec = vector;creates a type alias forvector`.
- Vec<int> is equivalent to vector<int>, making it more readable.
Output:
Elements in Vec<int>: 1 2 3 4 5
Summary Table of Template Examples
Example | Description |
---|---|
Basic Function Template | Defines a function that works with any data type |
Function Template with Multiple Parameters | Function template with more than one type parameter |
Basic Class Template | Defines a generic class for handling any data type |
Class Template with Multiple Parameters | Class template with multiple type parameters |
Template Specialization | Provides custom implementations for specific data types |
Function Template Specialization | Specialized function template for a particular type |
Non-type Template Parameters | Template with an integer parameter instead of a type |
Default Type Parameters | Template with a default type parameter |
Template Functions with auto | Uses auto and decltype for automatic return type deduction |
Template Aliases | Simplifies complex type names with aliases |
Key Takeaways
- Function templates and class templates allow for generic programming in C++, enabling the creation of reusable, type-independent code.
- Templates can handle multiple types, non-type parameters (like integers), and can have default types for added flexibility.
- Template specialization enables specific implementations for certain types, making generic code even more adaptable.
- auto and decltype can infer return types, simplifying complex type definitions and enhancing readability.
- Template aliases help shorten long type names, making code easier to read and maintain.