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.
template
keyword signals the start of a class template definition.<>
) to represent data types that the class can work with. These placeholders are typically named using uppercase letters like T
.
#include
template
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 intPair(5, 10);
std::cout << "First element of intPair: " << intPair.getFirst() << std::endl;
std::cout << "Second element of intPair: " << intPair.getSecond() << std::endl;
Pair 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
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.main()
function, we instantiate Pair
with different data types (int
and double
) using Pair<int>
and Pair<double>
.Pair
with specific data types and access their elements using member functions.
#include
#include // for std::out_of_range
template
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 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 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
Array
class is a template class that represents a dynamic array of elements of type T
.operator[]
for element access.main
function:Array
of integers is created and initialized with values 0, 10, 20, 30, 40
.Array
of doubles is created and initialized with values 3.14, 2.72, 1.6
.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 allows you to provide a customized implementation for specific template arguments while leaving others generic.
#include
template
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
class Pair {
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 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 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
Pair
with two template parameters T
and U
.T, T
).T
.main()
function, we instantiate Pair
with different data types and verify that the partial specialization works as expected.Template template parameters allow class templates to accept other class templates as arguments.
#include
template
class Container {
public:
Container(T value) : data(value) {}
void print() const { std::cout << data << std::endl; }
private:
T data;
};
template class C, typename T>
class Wrapper {
public:
Wrapper(T value) : container(value) {}
void print() const { container.print(); }
private:
C container;
};
int main() {
Wrapper wrapper1(42);
wrapper1.print();
Wrapper wrapper2(3.14);
wrapper2.print();
return 0;
}
// output //
42
3.14
Container
representing a simple container holding a value.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.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 allow class templates to accept non-type values as template arguments.
#include
template
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
Array
representing a fixed-size array.Size
is a non-type parameter representing the size of the array.main()
function, we instantiate Array
with different sizes, demonstrating the use of non-type template parameters.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.
#include
template
class MyClass {
private:
T data;
public:
MyClass(T value) : data(value) {}
friend class FriendClass;
};
class FriendClass {
public:
template
void accessData(MyClass& obj) {
std::cout << "Accessing data from FriendClass: " << obj.data << std::endl;
}
};
int main() {
MyClass obj(42);
FriendClass fc;
fc.accessData(obj);
return 0;
}
// output //
Accessing data from FriendClass: 42
FriendClass
is declared as a friend of the MyClass
template.FriendClass
, we define a member function accessData
that takes a MyClass
object as a parameter.FriendClass
is a friend of MyClass
, it can access the private member data
of MyClass
.main()
function, we instantiate MyClass<int>
and FriendClass
, then call accessData
to access the private member of MyClass
.
#include
template
class MyClass;
template
void accessData(MyClass& obj);
template
class MyClass {
private:
T data;
public:
MyClass(T value) : data(value) {}
friend void accessData(MyClass& obj);
};
template
void accessData(MyClass& obj) {
std::cout << "Accessing data from friend function: " << obj.data << std::endl;
}
int main() {
MyClass obj(42);
accessData(obj);
return 0;
}
// output //
Accessing data from friend function: 42
accessData
is declared as a friend function of the MyClass
template.accessData
before the definition of MyClass
to tell the compiler that accessData
is a friend function.MyClass
template, we declare accessData
as a friend function using friend void accessData<T>(MyClass<T>& obj);
.accessData
is a friend of MyClass
, it can access the private member data
of MyClass
.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 !❤️