Class Templates
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 65 of 104.
Just as function templates let you write type-independent functions, class templates let you create type-independent classes. This is how containers like std::vector work - you define the structure once, and the compiler generates specific versions for each type you use.
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};
int main() {
Box<int> intBox(42);
Box<std::string> strBox("Hello");
std::cout << intBox.getValue() << std::endl; // 42
std::cout << strBox.getValue() << std::endl; // Hello
}Unlike function templates, you must explicitly specify the type when creating objects from class templates using angle brackets. The compiler then generates a complete class definition for that specific type.
Class templates can have multiple type parameters and even non-type parameters like integers:
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
T& operator[](int index) { return data[index]; }
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Array of 5 doubles
arr[0] = 3.14;When defining member functions outside the class, you must repeat the template declaration:
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Class templates form the foundation of generic programming in C++, enabling you to write reusable data structures that work with any type.
Challenge
EasyLet's build a generic storage system using class templates to create a flexible container that can hold any type of data. You'll organize your template class in a header file and demonstrate its versatility with different types in your main program.
You'll create two files:
Storage.h: Define a class template calledStoragethat acts as a simple container for a single value with some useful operations:Your
Storagetemplate should have:- A private member to hold the stored value
- A constructor that takes an initial value
- A
getValue()method that returns the stored value - A
setValue()method that updates the stored value - An
isEmpty()method that returnstrueif the value equals the default-constructed value of type T,falseotherwise
Also create a class template called
Pairwith two type parameters that stores two related values:- Private members for the first and second values (of potentially different types)
- A constructor that initializes both values
- Methods
getFirst()andgetSecond()to retrieve each value - A
display()method that prints:(<first>, <second>)
main.cpp: Read four inputs (each on a separate line):- An integer value
- A new integer value
- A string value
- A double value
Demonstrate your templates by:
- Creating a
Storage<int>with the first integer, printing:Int storage: <value> - Updating it with the second integer using
setValue(), then printing:Updated: <value> - Creating a
Storage<std::string>with the string input, printing:String storage: <value> - Checking if the string storage is empty and printing:
Is empty: <true/false>(printtrueorfalse) - Creating a
Pair<std::string, double>with the string and double values, then callingdisplay() - Creating a
Pair<int, int>with both integer inputs, then callingdisplay()
For example, with inputs 42, 100, Hello, and 3.14:
Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)Notice how the same Storage template works seamlessly with integers and strings, and how Pair can combine different types together. The compiler generates separate class definitions for each type combination you use. Remember to use the template <typename T> syntax for single-type templates and template <typename T, typename U> for templates with multiple type parameters.
Cheat sheet
Class templates allow you to create type-independent classes. The compiler generates specific versions for each type you use.
Basic Class Template
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};When creating objects from class templates, you must explicitly specify the type using angle brackets:
Box<int> intBox(42);
Box<std::string> strBox("Hello");Multiple Type Parameters
Class templates can have multiple type parameters:
template <typename T, typename U>
class Pair {
T first;
U second;
public:
Pair(T f, U s) : first(f), second(s) {}
};Non-Type Parameters
Templates can also accept non-type parameters like integers:
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Array of 5 doublesDefining Member Functions Outside the Class
When defining member functions outside the class, repeat the template declaration:
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Try it yourself
#include <iostream>
#include <string>
#include "Storage.h"
using namespace std;
int main() {
// Read inputs
int intValue1;
int intValue2;
string strValue;
double doubleValue;
cin >> intValue1;
cin >> intValue2;
cin >> strValue;
cin >> doubleValue;
// TODO: Create Storage<int> with first integer and print: Int storage: <value>
// TODO: Update with second integer using setValue(), print: Updated: <value>
// TODO: Create Storage<std::string> with string input, print: String storage: <value>
// TODO: Check if string storage is empty, print: Is empty: true/false
// TODO: Create Pair<std::string, double> with string and double, call display()
// TODO: Create Pair<int, int> with both integers, call display()
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container