Class_templates
Class Templates
Section titled “Class Templates”Class templates create generic classes that work with any type, enabling type-safe containers and custom generic data structures.
Introduction
Section titled “Introduction”Class templates are similar to function templates but define how entire classes can be parameterized. The standard library containers like std::vector and std::map are class templates.
Basic Class Template
Section titled “Basic Class Template”Simple Box Class
Section titled “Simple Box Class”#include <iostream>#include <string>
template<typename T>class Box {private: T value_;
public: explicit Box(T v) : value_(v) {}
T getValue() const { return value_; } void setValue(T v) { value_ = v; }
void print() const { std::cout << "Box contains: " << value_ << "\n"; }};
int main() { Box<int> intBox(42); Box<std::string> strBox("Hello"); Box<double> dblBox(3.14);
intBox.print(); // Box contains: 42 strBox.print(); // Box contains: Hello dblBox.print(); // Box contains: 3.14
return 0;}Multiple Template Parameters
Section titled “Multiple Template Parameters”Pair Class
Section titled “Pair Class”#include <iostream>#include <string>#include <utility>
template<typename T1, typename T2>class Pair {private: T1 first_; T2 second_;
public: Pair() : first_(T1{}), second_(T2{}) {} Pair(T1 first, T2 second) : first_(first), second_(second) {}
T1& first() { return first_; } const T1& first() const { return first_; }
T2& second() { return second_; } const T2& second() const { return second_; }
void print() const { std::cout << "(" << first_ << ", " << second_ << ")\n"; }};
int main() { Pair<int, double> p1(1, 2.5); Pair<std::string, int> p2("Age", 30);
p1.print(); // (1, 2.5) p2.print(); // (Age, 30)
return 0;}Default Template Parameters
Section titled “Default Template Parameters”#include <iostream>#include <string>#include <utility>
template<typename T1, typename T2 = std::string>class Record { T1 key_; T2 value_;
public: Record(T1 k, T2 v) : key_(k), value_(v) {}
T1 getKey() const { return key_; } T2 getValue() const { return value_; }};
int main() { Record<int> r1(1, "One"); // Record<int, std::string> Record<int, int> r2(2, 42); // Both specified
return 0;}Member Function Templates
Section titled “Member Function Templates”#include <iostream>
template<typename T>class Container { T data_;
public: explicit Container(T d) : data_(d) {}
// Regular member function void print() const { std::cout << data_ << "\n"; }
// Member function template template<typename U> U convert() const { return static_cast<U>(data_); }
// Member function template with different type template<typename U> Container<U> transform(U (*fn)(T)) { return Container<U>(fn(data_)); }};
int main() { Container<int> c(42); c.print();
// Convert to double double d = c.convert<double>(); std::cout << d << "\n"; // 42
// Transform auto c2 = c.transform([](int x) { return x * 2.0; }); c2.print(); // 84
return 0;}Static Members in Templates
Section titled “Static Members in Templates”#include <iostream>
template<typename T>class Counter {private: static int count_; // Shared across all instances of Counter<T>
public: Counter() { ++count_; } ~Counter() { --count_; }
static int getCount() { return count_; }};
// Define static member for each instantiationtemplate<typename T>int Counter<T>::count_ = 0;
int main() { Counter<int> c1, c2, c3; std::cout << Counter<int>::getCount() << "\n"; // 3
Counter<double> d1; std::cout << Counter<int>::getCount() << "\n"; // Still 3 std::cout << Counter<double>::getCount() << "\n"; // 1
return 0;}Template Classes and Inheritance
Section titled “Template Classes and Inheritance”Base Class Template
Section titled “Base Class Template”#include <iostream>
template<typename T>class Base {protected: T value_;public: Base(T v) : value_(v) {} virtual void print() const = 0;};
template<typename T>class Derived : public Base<T> {public: Derived(T v) : Base<T>(v) {}
void print() const override { std::cout << "Derived: " << Base<T>::value_ << "\n"; }};
int main() { Derived<int> d(42); d.print(); // Derived: 42
return 0;}Specialization with Inheritance
Section titled “Specialization with Inheritance”#include <iostream>#include <string>
template<typename T>class Storage {protected: T data_;public: Storage(T d) : data_(d) {} virtual void save() const = 0;};
// Generic implementationtemplate<typename T>class FileStorage : public Storage<T> { std::string filename_;public: FileStorage(T d, const std::string& f) : Storage<T>(d), filename_(f) {}
void save() const override { std::cout << "Saving to " << filename_ << ": " << this->data_ << "\n"; }};
// Specialization for pointerstemplate<typename T>class FileStorage<T*> : public Storage<T*> { std::string filename_;public: FileStorage(T* d, const std::string& f) : Storage<T*>(d), filename_(f) {}
void save() const override { std::cout << "Saving pointer to " << filename_ << "\n"; }};
int main() { FileStorage<int> fs(42, "data.txt"); fs.save(); // Saving to data.txt: 42
int x = 100; FileStorage<int*> fp(&x, "ptr.txt"); fp.save(); // Saving pointer to ptr.txt
return 0;}Nested Templates
Section titled “Nested Templates”#include <iostream>#include <vector>
template<typename T>class Matrix { std::vector<std::vector<T>> data_; size_t rows_, cols_;
public: Matrix(size_t r, size_t c, T init = T{}) : rows_(r), cols_(c) { data_.resize(r); for (auto& row : data_) { row.resize(c, init); } }
T& at(size_t r, size_t c) { return data_[r][c]; } const T& at(size_t r, size_t c) const { return data_[r][c]; }
// Nested iterator class Iterator { Matrix& matrix_; size_t row_, col_; public: Iterator(Matrix& m, size_t r, size_t c) : matrix_(m), row_(r), col_(c) {}
T& operator*() { return matrix_.at(row_, col_); }
Iterator& operator++() { ++col_; if (col_ >= matrix_.cols()) { col_ = 0; ++row_; } return *this; }
bool operator!=(const Iterator& other) const { return row_ != other.row_ || col_ != other.col_; } };
Iterator begin() { return Iterator(*this, 0, 0); } Iterator end() { return Iterator(*this, rows_, 0); }};
int main() { Matrix<int> m(3, 3, 0); m.at(0, 0) = 1; m.at(1, 1) = 2; m.at(2, 2) = 3;
for (auto& val : m) { std::cout << val << " "; } std::cout << "\n";
return 0;}Friends and Operators
Section titled “Friends and Operators”Friend Function Templates
Section titled “Friend Function Templates”#include <iostream>
template<typename T>class Point { T x_, y_;
public: Point(T x, T y) : x_(x), y_(y) {}
// Friend function template declaration template<typename U> friend std::ostream& operator<<(std::ostream& os, const Point<U>& p) { os << "(" << p.x_ << ", " << p.y_ << ")"; return os; }};
int main() { Point<int> p1(1, 2); Point<double> p2(1.5, 2.5);
std::cout << p1 << "\n"; // (1, 2) std::cout << p2 << "\n"; // (1.5, 2.5)
return 0;}Operator Overloading
Section titled “Operator Overloading”#include <iostream>
template<typename T>class Vector2D { T x_, y_;
public: Vector2D(T x, T y) : x_(x), y_(y) {}
// Addition operator Vector2D operator+(const Vector2D& other) const { return Vector2D(x_ + other.x_, y_ + other.y_); }
// Scalar multiplication Vector2D operator*(T scalar) const { return Vector2D(x_ * scalar, y_ * scalar); }
// Stream output friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) { os << "(" << v.x_ << ", " << v.y_ << ")"; return os; }};
int main() { Vector2D<int> v1(1, 2); Vector2D<int> v2(3, 4);
Vector2D<int> v3 = v1 + v2; std::cout << v3 << "\n"; // (4, 6)
Vector2D<int> v4 = v1 * 3; std::cout << v4 << "\n"; // (3, 6)
return 0;}Type Aliases and Traits
Section titled “Type Aliases and Traits”#include <iostream>#include <vector>#include <type_traits>
template<typename T>class Container { std::vector<T> data_;
public: using value_type = T; // Type alias
void add(const T& value) { data_.push_back(value); }
const T& get(size_t index) const { return data_[index]; }
size_t size() const { return data_.size(); }};
int main() { Container<int> c; c.add(1); c.add(2); c.add(3);
// Use type alias Container<int>::value_type x = c.get(0); std::cout << x << "\n";
// Use with type traits static_assert(std::is_same_v<Container<int>::value_type, int>);
return 0;}CRTP (Curiously Recurring Template Pattern)
Section titled “CRTP (Curiously Recurring Template Pattern)”#include <iostream>
template<typename Derived>class Base {public: void interface() { // Call derived class method static_cast<Derived*>(this)->implementation(); }
void print() { std::cout << "Base::print called\n"; }};
class Derived : public Base<Derived> {public: void implementation() { std::cout << "Derived::implementation called\n"; }};
int main() { Derived d; d.interface(); // Calls Derived::implementation d.print(); // Calls Base::print
return 0;}Key Takeaways
Section titled “Key Takeaways”- Class templates enable generic container types
- Support multiple template parameters with defaults
- Member function templates enable flexible operations
- Static members are per-template-instantiation
- Friend function templates enable operator overloading
- CRTP enables compile-time polymorphism
- Type aliases improve code readability