Class Templates

Class templates in C++ allow you to define generic classes that can work with any data type. Instead of writing multiple class definitions for different data types, you can write a single class template that works for all types.

Basics

  • Template Keyword: The template keyword signals the start of a class template definition.
  • Template Parameters: You define placeholders within angle brackets (<>) to represent data types that the class can work with. These placeholders are typically named using uppercase letters like T.
  • Template Arguments: When using the class template, you specify the actual data types that you want the class to work with. These arguments are provided within angle brackets after the class name.
  • Class Body: The class body defines the member variables, member functions, and their functionalities using the template parameters as if they were regular data types.

Why Use Class Templates?

  • Reusability: Class templates promote code reuse by allowing you to write a single generic class that works with multiple data types.
  • Flexibility: With class templates, you can write more flexible and generic code that adapts to different data types.
  • Simplicity: Class templates simplify code maintenance by reducing the need for redundant class definitions for each data type.

Basic Syntax and Usage of Class Templates

				
					#include <iostream>

template <typename T>
class Pair {
private:
    T first;
    T second;
public:
    Pair(T a, T b) : first(a), second(b) {}
    T getFirst() const { return first; }
    T getSecond() const { return second; }
};

int main() {
    Pair<int> intPair(5, 10);
    std::cout << "First element of intPair: " << intPair.getFirst() << std::endl;
    std::cout << "Second element of intPair: " << intPair.getSecond() << std::endl;

    Pair<double> doublePair(3.5, 2.8);
    std::cout << "First element of doublePair: " << doublePair.getFirst() << std::endl;
    std::cout << "Second element of doublePair: " << doublePair.getSecond() << std::endl;

    return 0;
}

				
			
				
					// output //
First element of intPair: 5
Second element of intPair: 10
First element of doublePair: 3.5
Second element of doublePair: 2.8

				
			

Explanation:

  • Class Template Definition:

    • We define a class template named Pair using the template <typename T> syntax.
    • typename T declares T as a template parameter, representing the data type of the elements in the pair.

Class Template Usage:

  • In the main() function, we instantiate Pair with different data types (int and double) using Pair<int> and Pair<double>.
  • We create instances of Pair with specific data types and access their elements using member functions.

Array Class Template

				
					#include <iostream>
#include <stdexcept> // for std::out_of_range

template <typename T>
class Array {
private:
  T* data;  // Array of type T
  int size;  // Size of the array

public:
  // Constructor (default and with size)
  Array(int size = 0) : size(size) {
    if (size > 0) {
      data = new T[size];
    } else {
      data = nullptr;
    }
  }

  // Destructor
  ~Array() {
    if (data) {
      delete[] data;
    }
  }

  // Access element at index
  T& operator[](int index) {
    if (index >= 0 && index < size) {
      return data[index];
    }
    throw std::out_of_range("Index out of bounds");
  }

  // Get array size
  int getSize() const { return size; }
};

int main() {
  // Array of integers
  Array<int> intArray(5);
  for (int i = 0; i < intArray.getSize(); ++i) {
    intArray[i] = i * 10;
  }

  std::cout << "Integer array: ";
  for (int i = 0; i < intArray.getSize(); ++i) {
    std::cout << intArray[i] << " ";
  }
  std::cout << std::endl;

  // Array of doubles
  Array<double> doubleArray(3);
  doubleArray[0] = 3.14;
  doubleArray[1] = 2.72;
  doubleArray[2] = 1.6;

  std::cout << "Double array: ";
  for (int i = 0; i < doubleArray.getSize(); ++i) {
    std::cout << doubleArray[i] << " ";
  }
  std::cout << std::endl;

  return 0;
}

				
			
				
					// output //
Integer array: 0 10 20 30 40 
Double array: 3.14 2.72 1.6 

				
			

Explanation

  • The Array class is a template class that represents a dynamic array of elements of type T.
  • It has a constructor to initialize the array with a given size, a destructor to release memory, and an overloaded operator[] for element access.
  • In the main function:
    • An Array of integers is created and initialized with values 0, 10, 20, 30, 40.
    • An Array of doubles is created and initialized with values 3.14, 2.72, 1.6.
    • Elements of both arrays are accessed and printed.

Advanced Techniques with Class Templates

In this section, we explore advanced techniques for designing and using class templates in C++. These techniques include partial specialization, template template parameters, and non-type template parameters.

Partial Specialization of Class Templates

Partial specialization allows you to provide a customized implementation for specific template arguments while leaving others generic.

				
					#include <iostream>

template <typename T, typename U>
class Pair {
public:
    Pair(T a, U b) : first(a), second(b) {}
    T getFirst() const { return first; }
    U getSecond() const { return second; }
private:
    T first;
    U second;
};

template <typename T>
class Pair<T, T> {
public:
    Pair(T a, T b) : first(a), second(b) {}
    T getFirst() const { return first; }
    T getSecond() const { return second; }
private:
    T first;
    T second;
};

int main() {
    Pair<int, double> pair1(5, 3.14);
    std::cout << "First element of pair1: " << pair1.getFirst() << std::endl;
    std::cout << "Second element of pair1: " << pair1.getSecond() << std::endl;

    Pair<int, int> pair2(10, 20);
    std::cout << "First element of pair2: " << pair2.getFirst() << std::endl;
    std::cout << "Second element of pair2: " << pair2.getSecond() << std::endl;

    return 0;
}

				
			
				
					// output //
First element of pair1: 5
Second element of pair1: 3.14
First element of pair2: 10
Second element of pair2: 20

				
			

Explanation:

  • In this example, we define a class template Pair with two template parameters T and U.
  • We provide a partial specialization for the case where both template parameters are the same type (T, T).
  • Inside the partial specialization, both elements of the pair have the same type T.
  • In the main() function, we instantiate Pair with different data types and verify that the partial specialization works as expected.

Template Template Parameters

Template template parameters allow class templates to accept other class templates as arguments.

				
					#include <iostream>

template <typename T>
class Container {
public:
    Container(T value) : data(value) {}
    void print() const { std::cout << data << std::endl; }
private:
    T data;
};

template <template <typename> class C, typename T>
class Wrapper {
public:
    Wrapper(T value) : container(value) {}
    void print() const { container.print(); }
private:
    C<T> container;
};

int main() {
    Wrapper<Container, int> wrapper1(42);
    wrapper1.print();

    Wrapper<Container, double> wrapper2(3.14);
    wrapper2.print();

    return 0;
}

				
			
				
					// output //
42
3.14

				
			

Explanation:

  • In this example, we define a class template Container representing a simple container holding a value.
  • We define another class template Wrapper that wraps a Container and provides additional functionality.
  • Wrapper takes two template parameters: C, which is a template template parameter representing the container type, and T, which is the data type.
  • In the main() function, we instantiate Wrapper with Container as the container type and different data types, demonstrating the flexibility of template template parameters.

Non-type Template Parameters

Non-type template parameters allow class templates to accept non-type values as template arguments.

				
					#include <iostream>

template <int Size>
class Array {
public:
    void printSize() const { std::cout << "Size of array: " << Size << std::endl; }
private:
    int data[Size];
};

int main() {
    Array<5> array1;
    array1.printSize();

    Array<10> array2;
    array2.printSize();

    return 0;
}

				
			
				
					// output //
Size of array: 5
Size of array: 10

				
			

Explanation:

  • In this example, we define a class template Array representing a fixed-size array.
  • The template parameter Size is a non-type parameter representing the size of the array.
  • In the main() function, we instantiate Array with different sizes, demonstrating the use of non-type template parameters.

Template Friend Classes/Functions

In C++, template friend classes or functions allow templates to grant access to their private or protected members to specific non-template classes or functions.

Template Friend Classes

				
					#include <iostream>

template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T value) : data(value) {}
    friend class FriendClass;
};

class FriendClass {
public:
    template <typename T>
    void accessData(MyClass<T>& obj) {
        std::cout << "Accessing data from FriendClass: " << obj.data << std::endl;
    }
};

int main() {
    MyClass<int> obj(42);
    FriendClass fc;
    fc.accessData(obj);
    return 0;
}

				
			
				
					// output //
Accessing data from FriendClass: 42

				
			

Explanation:

  • In this example, FriendClass is declared as a friend of the MyClass template.
  • Inside FriendClass, we define a member function accessData that takes a MyClass object as a parameter.
  • Since FriendClass is a friend of MyClass, it can access the private member data of MyClass.
  • In the main() function, we instantiate MyClass<int> and FriendClass, then call accessData to access the private member of MyClass.

Template Friend Functions

				
					#include <iostream>

template <typename T>
class MyClass;

template <typename T>
void accessData(MyClass<T>& obj);

template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T value) : data(value) {}
    friend void accessData<T>(MyClass<T>& obj);
};

template <typename T>
void accessData(MyClass<T>& obj) {
    std::cout << "Accessing data from friend function: " << obj.data << std::endl;
}

int main() {
    MyClass<int> obj(42);
    accessData(obj);
    return 0;
}

				
			
				
					// output //
Accessing data from friend function: 42

				
			

Explanation:

  • In this example, accessData is declared as a friend function of the MyClass template.
  • We forward declare accessData before the definition of MyClass to tell the compiler that accessData is a friend function.
  • Inside the MyClass template, we declare accessData as a friend function using friend void accessData<T>(MyClass<T>& obj);.
  • Since accessData is a friend of MyClass, it can access the private member data of MyClass.
  • In the main() function, we instantiate MyClass<int> and call the accessData function to access the private member of MyClass.

Class templates in C++ are powerful tools for writing generic classes that can work with any data type. By mastering class templates and understanding advanced techniques such as specialization, programmers can write more versatile and efficient code that adapts to a wide range of requirements and use cases. Class templates promote code reuse, flexibility, and simplicity, making them an essential feature of modern C++ programming.Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India