cpp
Advanced Templates
03_Advanced_Templatesāļøcpp
/**
* ============================================================
* C++ TEMPLATE ADVANCED TOPICS
* ============================================================
*
* This file covers:
* - Template metaprogramming basics
* - SFINAE (Substitution Failure Is Not An Error)
* - Type traits
* - constexpr with templates
* - Template aliases
* - CRTP (Curiously Recurring Template Pattern)
*
* Compile: g++ -std=c++17 -Wall 01_advanced_templates.cpp -o adv_templates
* Run: ./adv_templates
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
#include <array>
using namespace std;
// ============================================================
// PART 1: TEMPLATE METAPROGRAMMING - COMPILE TIME COMPUTATION
// ============================================================
// Factorial at compile time
template <unsigned int N>
struct Factorial {
static constexpr unsigned long long value = N * Factorial<N-1>::value;
};
// Base case specialization
template <>
struct Factorial<0> {
static constexpr unsigned long long value = 1;
};
// Fibonacci at compile time
template <unsigned int N>
struct Fibonacci {
static constexpr unsigned long long value =
Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
template <>
struct Fibonacci<0> {
static constexpr unsigned long long value = 0;
};
template <>
struct Fibonacci<1> {
static constexpr unsigned long long value = 1;
};
// Power at compile time
template <int Base, unsigned int Exp>
struct Power {
static constexpr long long value = Base * Power<Base, Exp-1>::value;
};
template <int Base>
struct Power<Base, 0> {
static constexpr long long value = 1;
};
// ============================================================
// PART 2: TYPE TRAITS - COMPILE TIME TYPE INFORMATION
// ============================================================
// Custom type traits
template <typename T>
struct IsPointer {
static constexpr bool value = false;
};
template <typename T>
struct IsPointer<T*> {
static constexpr bool value = true;
};
template <typename T>
struct RemovePointer {
using type = T;
};
template <typename T>
struct RemovePointer<T*> {
using type = T;
};
// Check if two types are the same
template <typename T, typename U>
struct IsSame {
static constexpr bool value = false;
};
template <typename T>
struct IsSame<T, T> {
static constexpr bool value = true;
};
// ============================================================
// PART 3: SFINAE - ENABLE/DISABLE TEMPLATES
// ============================================================
// Using enable_if to enable for specific types
template <typename T>
typename enable_if<is_integral<T>::value, T>::type
doubleValue(T value) {
cout << "Integer version: ";
return value * 2;
}
template <typename T>
typename enable_if<is_floating_point<T>::value, T>::type
doubleValue(T value) {
cout << "Float version: ";
return value * 2.0;
}
// SFINAE with decltype
template <typename T>
auto hasSize(int) -> decltype(declval<T>().size(), true_type{}) {
return {};
}
template <typename T>
false_type hasSize(...) {
return {};
}
// More readable with void_t (C++17)
template <typename T, typename = void>
struct HasBeginEnd : false_type {};
template <typename T>
struct HasBeginEnd<T, void_t<decltype(declval<T>().begin()),
decltype(declval<T>().end())>>
: true_type {};
// ============================================================
// PART 4: TEMPLATE ALIASES
// ============================================================
// Simple alias
template <typename T>
using Ptr = T*;
template <typename T>
using Vec = vector<T>;
// Alias for complex types
template <typename K, typename V>
using StringMap = vector<pair<K, V>>;
// Alias with enable_if
template <typename T>
using EnableIfNumeric = enable_if_t<is_arithmetic_v<T>, T>;
// ============================================================
// PART 5: CRTP - CURIOUSLY RECURRING TEMPLATE PATTERN
// ============================================================
// Static polymorphism using CRTP
template <typename Derived>
class Comparable {
public:
bool operator!=(const Derived& other) const {
return !(static_cast<const Derived&>(*this) == other);
}
bool operator<=(const Derived& other) const {
return !(other < static_cast<const Derived&>(*this));
}
bool operator>(const Derived& other) const {
return other < static_cast<const Derived&>(*this);
}
bool operator>=(const Derived& other) const {
return !(static_cast<const Derived&>(*this) < other);
}
};
class Point : public Comparable<Point> {
public:
int x, y;
Point(int x = 0, int y = 0) : x(x), y(y) {}
// Only need to implement == and <
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
bool operator<(const Point& other) const {
if (x != other.x) return x < other.x;
return y < other.y;
}
};
// CRTP for static interface
template <typename Derived>
class Shape {
public:
double area() const {
return static_cast<const Derived*>(this)->areaImpl();
}
void draw() const {
static_cast<const Derived*>(this)->drawImpl();
}
};
class Circle : public Shape<Circle> {
double radius;
public:
Circle(double r) : radius(r) {}
double areaImpl() const {
return 3.14159 * radius * radius;
}
void drawImpl() const {
cout << "Drawing circle with radius " << radius << endl;
}
};
class Square : public Shape<Square> {
double side;
public:
Square(double s) : side(s) {}
double areaImpl() const {
return side * side;
}
void drawImpl() const {
cout << "Drawing square with side " << side << endl;
}
};
// ============================================================
// PART 6: CONSTEXPR WITH TEMPLATES
// ============================================================
template <typename T, size_t N>
constexpr T arraySum(const T (&arr)[N]) {
T sum = 0;
for (size_t i = 0; i < N; i++) {
sum += arr[i];
}
return sum;
}
template <typename T>
constexpr T constexprMax(T a, T b) {
return (a > b) ? a : b;
}
// Compile-time string length
constexpr size_t strLen(const char* str) {
return (*str == '\0') ? 0 : 1 + strLen(str + 1);
}
// ============================================================
// PART 7: IF CONSTEXPR (C++17)
// ============================================================
template <typename T>
auto processValue(T value) {
if constexpr (is_integral_v<T>) {
cout << "Processing integer: " << value << endl;
return value * 2;
} else if constexpr (is_floating_point_v<T>) {
cout << "Processing float: " << value << endl;
return value * 2.5;
} else if constexpr (is_same_v<T, string>) {
cout << "Processing string: " << value << endl;
return value + value;
} else {
cout << "Processing unknown type" << endl;
return value;
}
}
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ ADVANCED TEMPLATES" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Template Metaprogramming
// ========================================================
cout << "--- DEMO 1: COMPILE-TIME COMPUTATION ---" << endl << endl;
cout << "Factorial<0> = " << Factorial<0>::value << endl;
cout << "Factorial<5> = " << Factorial<5>::value << endl;
cout << "Factorial<10> = " << Factorial<10>::value << endl;
cout << "Factorial<20> = " << Factorial<20>::value << endl;
cout << "\nFibonacci<0> = " << Fibonacci<0>::value << endl;
cout << "Fibonacci<10> = " << Fibonacci<10>::value << endl;
cout << "Fibonacci<20> = " << Fibonacci<20>::value << endl;
cout << "\nPower<2, 10> = " << Power<2, 10>::value << endl;
cout << "Power<3, 5> = " << Power<3, 5>::value << endl;
cout << "\nš” All computed at COMPILE TIME!" << endl;
cout << endl;
// ========================================================
// DEMO 2: Custom Type Traits
// ========================================================
cout << "--- DEMO 2: CUSTOM TYPE TRAITS ---" << endl << endl;
cout << "IsPointer<int>::value = " << IsPointer<int>::value << endl;
cout << "IsPointer<int*>::value = " << IsPointer<int*>::value << endl;
cout << "IsPointer<string>::value = " << IsPointer<string>::value << endl;
cout << "IsPointer<string*>::value = " << IsPointer<string*>::value << endl;
cout << "\nIsSame<int, int>::value = " << IsSame<int, int>::value << endl;
cout << "IsSame<int, double>::value = " << IsSame<int, double>::value << endl;
cout << "\nRemovePointer<int*>::type is int? "
<< IsSame<RemovePointer<int*>::type, int>::value << endl;
cout << endl;
// ========================================================
// DEMO 3: Standard Type Traits
// ========================================================
cout << "--- DEMO 3: STANDARD TYPE TRAITS ---" << endl << endl;
cout << "is_integral<int>::value = " << is_integral<int>::value << endl;
cout << "is_integral<double>::value = " << is_integral<double>::value << endl;
cout << "is_floating_point<double>::value = " << is_floating_point<double>::value << endl;
cout << "\nis_pointer<int>::value = " << is_pointer<int>::value << endl;
cout << "is_pointer<int*>::value = " << is_pointer<int*>::value << endl;
cout << "\nis_class<string>::value = " << is_class<string>::value << endl;
cout << "is_class<int>::value = " << is_class<int>::value << endl;
cout << "\nis_same<int, int32_t>::value = " << is_same<int, int32_t>::value << endl;
cout << endl;
// ========================================================
// DEMO 4: SFINAE
// ========================================================
cout << "--- DEMO 4: SFINAE ---" << endl << endl;
cout << doubleValue(5) << endl; // Integer version
cout << doubleValue(3.14) << endl; // Float version
cout << "\nhasSize<vector<int>>: " << decltype(hasSize<vector<int>>(0))::value << endl;
cout << "hasSize<int>: " << decltype(hasSize<int>(0))::value << endl;
cout << "\nHasBeginEnd<vector<int>>: " << HasBeginEnd<vector<int>>::value << endl;
cout << "HasBeginEnd<int>: " << HasBeginEnd<int>::value << endl;
cout << endl;
// ========================================================
// DEMO 5: Template Aliases
// ========================================================
cout << "--- DEMO 5: TEMPLATE ALIASES ---" << endl << endl;
Ptr<int> intPtr = new int(42);
Vec<string> names = {"Alice", "Bob", "Charlie"};
cout << "Ptr<int> value: " << *intPtr << endl;
cout << "Vec<string> contents: ";
for (const auto& name : names) {
cout << name << " ";
}
cout << endl;
delete intPtr;
cout << endl;
// ========================================================
// DEMO 6: CRTP
// ========================================================
cout << "--- DEMO 6: CRTP ---" << endl << endl;
Point p1(3, 4), p2(3, 5), p3(3, 4);
cout << "p1 == p3: " << (p1 == p3) << endl;
cout << "p1 != p2: " << (p1 != p2) << endl;
cout << "p1 < p2: " << (p1 < p2) << endl;
cout << "p1 <= p2: " << (p1 <= p2) << endl;
cout << "p1 > p2: " << (p1 > p2) << endl;
cout << "\nShapes:" << endl;
Circle c(5.0);
Square s(4.0);
c.draw();
cout << "Circle area: " << c.area() << endl;
s.draw();
cout << "Square area: " << s.area() << endl;
cout << endl;
// ========================================================
// DEMO 7: constexpr with Templates
// ========================================================
cout << "--- DEMO 7: CONSTEXPR ---" << endl << endl;
constexpr int arr[] = {1, 2, 3, 4, 5};
constexpr auto sum = arraySum(arr);
cout << "constexpr arraySum = " << sum << endl;
constexpr auto maxVal = constexprMax(10, 20);
cout << "constexpr max(10, 20) = " << maxVal << endl;
constexpr auto len = strLen("Hello");
cout << "constexpr strLen(\"Hello\") = " << len << endl;
// Use in static_assert
static_assert(Factorial<5>::value == 120, "Factorial failed!");
static_assert(strLen("test") == 4, "strLen failed!");
cout << "\nš” All verified at compile time with static_assert!" << endl;
cout << endl;
// ========================================================
// DEMO 8: if constexpr
// ========================================================
cout << "--- DEMO 8: IF CONSTEXPR ---" << endl << endl;
auto r1 = processValue(42);
cout << "Result: " << r1 << endl;
auto r2 = processValue(3.14);
cout << "Result: " << r2 << endl;
auto r3 = processValue(string("Hello"));
cout << "Result: " << r3 << endl;
cout << endl;
// ========================================================
// SUMMARY
// ========================================================
cout << "--- ADVANCED TEMPLATE CONCEPTS ---" << endl << endl;
cout << "Template Metaprogramming:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Computation at compile time" << endl;
cout << "⢠Uses recursion with specialization" << endl;
cout << "⢠Results embedded in executable" << endl;
cout << "\nSFINAE:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Substitution Failure Is Not An Error" << endl;
cout << "⢠Enable/disable templates based on types" << endl;
cout << "⢠Use enable_if, void_t, decltype" << endl;
cout << "\nCRTP:" << endl;
cout << "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā" << endl;
cout << "⢠Static polymorphism (no vtable)" << endl;
cout << "⢠class Derived : public Base<Derived>" << endl;
cout << "⢠Mixin functionality" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "ADVANCED TEMPLATES COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Implement compile-time:
* - IsPrime<N> - check if N is prime
* - GCD<A, B> - greatest common divisor
* - NthPrime<N> - Nth prime number
*
* 2. Create type traits:
* - IsContainer<T> - has begin(), end(), size()
* - IsCallable<T> - can be called with operator()
* - RemoveConst<T>, AddConst<T>
*
* 3. Implement CRTP for:
* - Singleton pattern
* - Object counter (track instances)
* - Cloneable interface
*
* 4. Use SFINAE to create:
* - toString() for any type (numeric, string, container)
* - serialize() that handles built-in and custom types
*/