Home C++ C++ generics tutorial with code examples

C++ generics tutorial with code examples

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++

  1. Function Templates: Write generic functions that can work with multiple data types.
  2. Class Templates: Define classes that can handle various data types.
  3. Template Specialization: Provide specific implementations for particular data types.
  4. 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.

You may also like