Advanced C++

Master advanced C++ concepts and modern features

Templates & Generic Programming

Templates are C++'s powerful feature for generic programming, allowing you to write code that works with multiple types while maintaining type safety and performance. They enable compile-time polymorphism and are the foundation of the Standard Template Library (STL).

Why Templates Matter:

Code Reusability

Write once, use with any type. No code duplication needed.

Zero Runtime Cost

Template instantiation happens at compile-time with no performance penalty.

Type Safety

Compile-time type checking prevents runtime errors.

Compile-time Computation

Enable template metaprogramming for advanced optimizations.

Function Templates:

function_templates.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <type_traits>
using namespace std;

// Basic function template
template<typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// Function template with multiple parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// Function template with default template parameter
template<typename T = int>
T square(T value) {
    return value * value;
}

// Variadic function template (C++11)
template<typename T>
T sum(T value) {
    return value;
}

template<typename T, typename... Args>
T sum(T first, Args... args) {
    return first + sum(args...);
}

// Template with SFINAE (Substitution Failure Is Not An Error)
template<typename T>
typename enable_if<is_arithmetic<T>::value, T>::type
safeAdd(T a, T b) {
    return a + b;
}

// Template specialization
template<>
const char* maximum<const char*>(const char* a, const char* b) {
    return (strcmp(a, b) > 0) ? a : b;
}

// Function template with concept (C++20)
#if __cplusplus >= 202002L
#include <concepts>

template<std::integral T>
T gcd(T a, T b) {
    while (b != 0) {
        T temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
#endif

int main() {
    // Basic usage
    cout << "Maximum of 10, 20: " << maximum(10, 20) << endl;
    cout << "Maximum of 3.14, 2.71: " << maximum(3.14, 2.71) << endl;
    
    // Multiple parameter types
    cout << "Add 5 + 3.14: " << add(5, 3.14) << endl;
    
    // Default template parameter
    cout << "Square of 5: " << square(5) << endl;
    cout << "Square of 3.14: " << square(3.14) << endl;
    
    // Variadic templates
    cout << "Sum of 1,2,3,4,5: " << sum(1, 2, 3, 4, 5) << endl;
    cout << "Sum of 1.1,2.2,3.3: " << sum(1.1, 2.2, 3.3) << endl;
    
    // SFINAE example
    cout << "Safe add 10 + 20: " << safeAdd(10, 20) << endl;
    
    // Template specialization
    cout << "Max string: " << maximum("apple", "banana") << endl;
    
#if __cplusplus >= 202002L
    // Concepts (C++20)
    cout << "GCD of 48, 18: " << gcd(48, 18) << endl;
#endif
    
    return 0;
}

Class Templates:

class_templates.cpp
#include <iostream>
#include <vector>
#include <memory>
#include <stdexcept>
using namespace std;

// Basic class template
template<typename T>
class Container {
private:
    vector<T> data;
    
public:
    void add(const T& item) { data.push_back(item); }
    T& get(size_t index) { return data.at(index); }
    size_t size() const { return data.size(); }
    bool empty() const { return data.empty(); }
    
    // Template member function
    template<typename U>
    void addConverted(const U& item) {
        data.push_back(static_cast<T>(item));
    }
};

// Template with multiple parameters and default values
template<typename T, size_t Size = 10>
class FixedArray {
private:
    T data[Size];
    size_t currentSize = 0;
    
public:
    void push_back(const T& value) {
        if (currentSize >= Size) {
            throw overflow_error("Array is full");
        }
        data[currentSize++] = value;
    }
    
    T& operator[](size_t index) {
        if (index >= currentSize) {
            throw out_of_range("Index out of range");
        }
        return data[index];
    }
    
    size_t size() const { return currentSize; }
    size_t capacity() const { return Size; }
};

// Template specialization for bool (space optimization)
template<size_t Size>
class FixedArray<bool, Size> {
private:
    vector<bool> data;  // std::vector<bool> is specialized for space efficiency
    
public:
    FixedArray() : data(Size, false) {}
    
    void set(size_t index, bool value) {
        if (index >= Size) throw out_of_range("Index out of range");
        data[index] = value;
    }
    
    bool get(size_t index) const {
        if (index >= Size) throw out_of_range("Index out of range");
        return data[index];
    }
    
    size_t size() const { return Size; }
};

// Advanced: Smart pointer-like template
template<typename T>
class UniquePtr {
private:
    T* ptr;
    
public:
    explicit UniquePtr(T* p = nullptr) : ptr(p) {}
    
    ~UniquePtr() { delete ptr; }
    
    // Move constructor
    UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) {
        other.ptr = nullptr;
    }
    
    // Move assignment
    UniquePtr& operator=(UniquePtr&& other) noexcept {
        if (this != &other) {
            delete ptr;
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        return *this;
    }
    
    // Disable copy
    UniquePtr(const UniquePtr&) = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;
    
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    T* get() const { return ptr; }
    bool operator!() const { return !ptr; }
    explicit operator bool() const { return ptr != nullptr; }
    
    T* release() {
        T* temp = ptr;
        ptr = nullptr;
        return temp;
    }
    
    void reset(T* p = nullptr) {
        delete ptr;
        ptr = p;
    }
};

// Template template parameter
template<typename T, template<typename> class Container>
class Stack {
private:
    Container<T> container;
    
public:
    void push(const T& item) { container.add(item); }
    T pop() {
        if (container.empty()) {
            throw runtime_error("Stack is empty");
        }
        T item = container.get(container.size() - 1);
        // Note: This is simplified - real implementation would need pop_back
        return item;
    }
    bool empty() const { return container.empty(); }
    size_t size() const { return container.size(); }
};

int main() {
    // Basic class template usage
    Container<int> intContainer;
    intContainer.add(10);
    intContainer.add(20);
    intContainer.addConverted(3.14);  // Template member function
    
    cout << "Container size: " << intContainer.size() << endl;
    cout << "First element: " << intContainer.get(0) << endl;
    
    // Fixed array template
    FixedArray<string, 5> names;
    names.push_back("Alice");
    names.push_back("Bob");
    names.push_back("Charlie");
    
    cout << "Names array size: " << names.size() << endl;
    cout << "First name: " << names[0] << endl;
    
    // Template specialization for bool
    FixedArray<bool, 8> flags;
    flags.set(0, true);
    flags.set(3, true);
    flags.set(7, true);
    
    cout << "Flag at index 0: " << flags.get(0) << endl;
    cout << "Flag at index 1: " << flags.get(1) << endl;
    
    // Smart pointer template
    UniquePtr<int> smartPtr(new int(42));
    cout << "Smart pointer value: " << *smartPtr << endl;
    
    // Move semantics
    UniquePtr<string> ptr1(new string("Hello"));
    UniquePtr<string> ptr2 = move(ptr1);
    cout << "Moved string: " << *ptr2 << endl;
    
    // Template template parameter
    Stack<int, Container> stack;
    stack.push(1);
    stack.push(2);
    stack.push(3);
    
    cout << "Stack size: " << stack.size() << endl;
    
    return 0;
}

Template Concepts (C++20):

Concepts provide a way to specify constraints on template parameters, making template code more readable and providing better error messages.

concepts_example.cpp
// C++20 Concepts Example
#include <iostream>
#include <concepts>
#include <type_traits>

// Define custom concepts
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;

template<typename T>
concept Printable = requires(T t) {
    std::cout << t;
};

template<typename T>
concept Comparable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a > b } -> std::convertible_to<bool>;
    { a == b } -> std::convertible_to<bool>;
};

// Using concepts with functions
template<Numeric T>
T multiply(T a, T b) {
    return a * b;
}

template<Printable T>
void print(const T& value) {
    std::cout << value << std::endl;
}

template<Comparable T>
T findMax(T a, T b, T c) {
    T max = a;
    if (b > max) max = b;
    if (c > max) max = c;
    return max;
}

// Concepts with classes
template<Numeric T>
class Calculator {
public:
    T add(T a, T b) { return a + b; }
    T subtract(T a, T b) { return a - b; }
    T multiply(T a, T b) { return a * b; }
    T divide(T a, T b) {
        static_assert(std::floating_point<T>, "Division requires floating point type");
        return a / b;
    }
};

// Abbreviated function template syntax (C++20)
auto quickAdd(Numeric auto a, Numeric auto b) {
    return a + b;
}

int main() {
    // Valid uses with concepts
    std::cout << multiply(5, 3) << std::endl;        // int
    std::cout << multiply(2.5, 1.5) << std::endl;    // double
    
    print("Hello World");   // string literal
    print(42);              // int
    print(3.14);            // double
    
    std::cout << findMax(10, 5, 8) << std::endl;
    std::cout << findMax(3.14, 2.71, 1.41) << std::endl;
    
    Calculator<double> calc;
    std::cout << calc.add(10.5, 5.5) << std::endl;
    std::cout << calc.divide(10.0, 3.0) << std::endl;
    
    // Abbreviated syntax
    std::cout << quickAdd(100, 200) << std::endl;
    std::cout << quickAdd(1.1, 2.2) << std::endl;
    
    // The following would cause compilation errors:
    // multiply("hello", "world");  // string is not Numeric
    // Calculator<std::string> calc; // string is not Numeric
    
    return 0;
}

Template Best Practices:

Do's

  • Use meaningful template parameter names
  • Provide default template arguments when appropriate
  • Use SFINAE or concepts for constraints
  • Specialize templates for specific types when needed
  • Use auto for return type deduction
  • Prefer variadic templates over overloading

Don'ts

  • Don't use templates when simple functions suffice
  • Avoid deep template instantiation hierarchies
  • Don't ignore compilation time impact
  • Avoid exposing template internals in headers
  • Don't use templates for every small function
  • Avoid complex SFINAE when concepts are available

Pro Tip: Templates are evaluated at compile-time, so complex template code can significantly increase compilation time. Use explicit instantiation and precompiled headers for frequently used templates.

Performance: Well-designed templates can produce code that's as fast as hand-written specialized code, sometimes even faster due to compiler optimizations enabled by template instantiation.

Smart Pointers & Memory Management

Smart pointers are modern C++ objects that automatically manage memory, providing exception safety and preventing common memory-related bugs like memory leaks, double deletion, and dangling pointers. They represent ownership semantics and are fundamental to RAII (Resource Acquisition Is Initialization).

Memory Management Evolution:

❌ C-style (Dangerous)

int* ptr = malloc(sizeof(int));

Manual memory management, prone to leaks

⚠️ Raw Pointers (Error-prone)

int* ptr = new int(42);

Must remember to delete, exception-unsafe

✅ Smart Pointers (Modern)

auto ptr = std::make_unique<int>(42);

Automatic cleanup, exception-safe, expressive

Types of Smart Pointers:

🔒

unique_ptr

Exclusive Ownership

  • Single owner of the resource
  • Move-only semantics
  • Zero overhead when optimized
  • Automatic deletion when destroyed
🤝

shared_ptr

Shared Ownership

  • Multiple owners allowed
  • Reference counting
  • Thread-safe reference counting
  • Deleted when last owner is destroyed
👁️

weak_ptr

Non-owning Observer

  • Observes shared_ptr without owning
  • Breaks circular references
  • Can check if resource still exists
  • Converts to shared_ptr when needed

Comprehensive Smart Pointer Examples:

comprehensive_smart_pointers.cpp
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <cassert>
using namespace std;

// Resource class for demonstration
class Resource {
private:
    string name;
    static int counter;
    int id;
    
public:
    Resource(const string& n) : name(n), id(++counter) {
        cout << "Resource [" << id << "] '" << name << "' created" << endl;
    }
    
    ~Resource() {
        cout << "Resource [" << id << "] '" << name << "' destroyed" << endl;
    }
    
    void use() const {
        cout << "Using resource [" << id << "] '" << name << "'" << endl;
    }
    
    const string& getName() const { return name; }
    int getId() const { return id; }
};

int Resource::counter = 0;

// Factory function using unique_ptr
unique_ptr<Resource> createResource(const string& name) {
    return make_unique<Resource>(name);
}

// Function that takes ownership
void processResource(unique_ptr<Resource> resource) {
    if (resource) {
        resource->use();
        cout << "Processing completed for " << resource->getName() << endl;
    }
    // resource automatically destroyed when function ends
}

// Function that observes without taking ownership
void observeResource(const Resource* resource) {
    if (resource) {
        cout << "Observing resource: " << resource->getName() << endl;
    }
}

// Example class using shared_ptr for aggregation
class ResourceManager {
private:
    vector<shared_ptr<Resource>> resources;
    
public:
    void addResource(shared_ptr<Resource> resource) {
        resources.push_back(resource);
        cout << "Added resource to manager. Total: " << resources.size() << endl;
    }
    
    void useAllResources() const {
        cout << "Using all managed resources:" << endl;
        for (const auto& resource : resources) {
            if (resource) {
                resource->use();
            }
        }
    }
    
    shared_ptr<Resource> getResource(int index) const {
        if (index >= 0 && index < resources.size()) {
            return resources[index];
        }
        return nullptr;
    }
    
    size_t size() const { return resources.size(); }
};

// Example of circular reference problem and solution
class Parent;
class Child;

class Parent {
public:
    string name;
    vector<shared_ptr<Child>> children;
    
    Parent(const string& n) : name(n) {
        cout << "Parent " << name << " created" << endl;
    }
    
    ~Parent() {
        cout << "Parent " << name << " destroyed" << endl;
    }
    
    void addChild(shared_ptr<Child> child);
};

class Child {
public:
    string name;
    weak_ptr<Parent> parent;  // Use weak_ptr to break circular reference
    
    Child(const string& n) : name(n) {
        cout << "Child " << name << " created" << endl;
    }
    
    ~Child() {
        cout << "Child " << name << " destroyed" << endl;
    }
    
    void setParent(shared_ptr<Parent> p) {
        parent = p;
    }
    
    void visitParent() {
        if (auto p = parent.lock()) {  // Convert weak_ptr to shared_ptr
            cout << "Child " << name << " visiting parent " << p->name << endl;
        } else {
            cout << "Child " << name << "'s parent is no longer available" << endl;
        }
    }
};

void Parent::addChild(shared_ptr<Child> child) {
    children.push_back(child);
    child->setParent(shared_from_this());  // This requires Parent to inherit from enable_shared_from_this
}

// Custom deleter example
void customDeleter(Resource* ptr) {
    cout << "Custom deleter called for " << ptr->getName() << endl;
    delete ptr;
}

int main() {
    cout << "=== UNIQUE_PTR EXAMPLES ===" << endl;
    
    // Basic unique_ptr usage
    {
        cout << "\n--- Basic Usage ---" << endl;
        auto resource1 = make_unique<Resource>("Database Connection");
        resource1->use();
        
        // Transfer ownership
        auto resource2 = move(resource1);
        assert(resource1 == nullptr);  // resource1 is now null
        
        if (resource2) {
            resource2->use();
        }
        
        // Factory function
        auto resource3 = createResource("Network Socket");
        processResource(move(resource3));  // Transfer ownership to function
        assert(resource3 == nullptr);  // resource3 is now null
    }
    
    cout << "\n=== SHARED_PTR EXAMPLES ===" << endl;
    
    // Basic shared_ptr usage
    {
        cout << "\n--- Shared Ownership ---" << endl;
        auto resource = make_shared<Resource>("Shared File Handle");
        cout << "Reference count: " << resource.use_count() << endl;
        
        ResourceManager manager;
        manager.addResource(resource);  // Manager shares ownership
        cout << "Reference count after adding to manager: " << resource.use_count() << endl;
        
        {
            auto anotherRef = resource;  // Another shared reference
            cout << "Reference count with additional ref: " << resource.use_count() << endl;
            anotherRef->use();
        }  // anotherRef goes out of scope
        
        cout << "Reference count after scope exit: " << resource.use_count() << endl;
        manager.useAllResources();
    }
    
    cout << "\n=== WEAK_PTR EXAMPLES ===" << endl;
    
    // Circular reference prevention
    {
        cout << "\n--- Breaking Circular References ---" << endl;
        
        auto parent = make_shared<Parent>("Alice");
        auto child1 = make_shared<Child>("Bob");
        auto child2 = make_shared<Child>("Carol");
        
        // Note: This simplified example doesn't use enable_shared_from_this
        // In real code, Parent should inherit from enable_shared_from_this<Parent>
        parent->children.push_back(child1);
        parent->children.push_back(child2);
        child1->setParent(parent);
        child2->setParent(parent);
        
        cout << "Parent reference count: " << parent.use_count() << endl;
        cout << "Child1 reference count: " << child1.use_count() << endl;
        
        child1->visitParent();
        child2->visitParent();
        
        // When parent goes out of scope, children can still check if parent exists
        {
            auto tempChild = child1;
            parent.reset();  // Release parent
            tempChild->visitParent();  // Should show parent is no longer available
        }
    }
    
    cout << "\n=== ADVANCED FEATURES ===" << endl;
    
    // Custom deleter
    {
        cout << "\n--- Custom Deleter ---" << endl;
        unique_ptr<Resource, decltype(&customDeleter)> customPtr(
            new Resource("Custom Managed"), customDeleter);
        customPtr->use();
    }
    
    // Array handling
    {
        cout << "\n--- Array Handling ---" << endl;
        auto intArray = make_unique<int[]>(5);
        for (int i = 0; i < 5; ++i) {
            intArray[i] = i * i;
        }
        
        cout << "Array values: ";
        for (int i = 0; i < 5; ++i) {
            cout << intArray[i] << " ";
        }
        cout << endl;
    }
    
    // Performance comparison
    {
        cout << "\n--- Performance Notes ---" << endl;
        cout << "unique_ptr size: " << sizeof(unique_ptr<Resource>) << " bytes" << endl;
        cout << "shared_ptr size: " << sizeof(shared_ptr<Resource>) << " bytes" << endl;
        cout << "weak_ptr size: " << sizeof(weak_ptr<Resource>) << " bytes" << endl;
        cout << "raw pointer size: " << sizeof(Resource*) << " bytes" << endl;
    }
    
    return 0;
}

Smart Pointer Decision Tree:

🤔 Which Smart Pointer to Use?

Single Owner?
Use unique_ptr
File handles, network connections
Multiple Owners?
Use shared_ptr
Cached objects, shared resources
Observer Only?
Use weak_ptr
Callbacks, cache observers

Common Patterns & Best Practices:

Factory Pattern

return make_unique<T>(args...);

Return unique_ptr from factory functions

Ownership Transfer

processData(std::move(ptr));

Use std::move to transfer unique_ptr ownership

Safe Observation

if (auto p = weak_ptr.lock()) { ... }

Always check weak_ptr before using

Exception Safety

auto ptr = make_unique<T>();

Prefer make_unique/make_shared over new

Performance Considerations:

Pointer Type Memory Overhead Performance Thread Safety Use Case
unique_ptr Zero overhead Same as raw pointer Not thread-safe Exclusive ownership
shared_ptr 2 pointers + ref count Atomic operations Reference counting is thread-safe Shared ownership
weak_ptr 2 pointers Lock operation overhead Thread-safe observation Non-owning observation

Avoid These Mistakes:

  • Don't use shared_ptr when unique_ptr suffices
  • Never store shared_ptr in member variables without considering cycles
  • Don't convert raw pointers to smart pointers randomly
  • Avoid get() unless interfacing with C APIs

Memory Safety Achievement: With smart pointers, you've eliminated manual memory management. Your code is now exception-safe, leak-free, and expresses ownership semantics clearly!

Exception Handling

Exception handling in C++ provides a structured way to handle runtime errors and exceptional conditions. It separates error handling code from normal program logic, making code more readable and maintainable while ensuring program stability.

Exception Handling Philosophy:

🎯 Separation of Concerns

Error handling logic is separated from business logic

🔄 Stack Unwinding

Automatic cleanup of local objects when exceptions occur

🛡️ Exception Safety

Guarantees about program state when exceptions are thrown

⚡ Performance

Zero-cost when no exceptions are thrown

Exception Safety Levels:

Basic Guarantee

No resource leaks, objects remain in valid state

Strong Guarantee

Operation succeeds completely or has no effect

No-throw Guarantee

Operation never throws exceptions

comprehensive_exception_handling.cpp
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include <system_error>
using namespace std;

// Custom exception hierarchy
class MathException : public std::exception {
protected:
    string message;
public:
    MathException(const string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

class DivisionByZeroException : public MathException {
public:
    DivisionByZeroException() : MathException("Division by zero is not allowed!") {}
};

class NegativeArgumentException : public MathException {
public:
    NegativeArgumentException(const string& operation) 
        : MathException(operation + " is not defined for negative numbers") {}
};

class OverflowException : public MathException {
public:
    OverflowException(const string& operation)
        : MathException(operation + " result is too large to compute") {}
};

// RAII resource management class
class FileManager {
private:
    string filename;
    unique_ptr<ofstream> file;
    
public:
    FileManager(const string& fname) : filename(fname) {
        file = make_unique<ofstream>(filename);
        if (!file->is_open()) {
            throw runtime_error("Failed to open file: " + filename);
        }
        cout << "File opened: " << filename << endl;
    }
    
    ~FileManager() {
        if (file && file->is_open()) {
            file->close();
            cout << "File closed: " << filename << endl;
        }
    }
    
    void write(const string& data) {
        if (!file || !file->is_open()) {
            throw runtime_error("File is not open for writing");
        }
        *file << data << endl;
        if (file->fail()) {
            throw runtime_error("Failed to write to file");
        }
    }
};

// Exception-safe calculator class
class SafeCalculator {
public:
    // Strong exception safety guarantee
    static double divide(double a, double b) {
        if (b == 0.0) {
            throw DivisionByZeroException();
        }
        return a / b;
    }
    
    // Strong exception safety guarantee  
    static long long factorial(int n) {
        if (n < 0) {
            throw NegativeArgumentException("Factorial");
        }
        if (n > 20) {
            throw OverflowException("Factorial");
        }
        
        long long result = 1;
        for (int i = 1; i <= n; i++) {
            result *= i;
        }
        return result;
    }
    
    // Function with multiple exception types
    static double power(double base, int exponent) {
        if (base == 0.0 && exponent < 0) {
            throw DivisionByZeroException();
        }
        if (exponent < 0) {
            throw NegativeArgumentException("Negative exponent");
        }
        
        double result = 1.0;
        for (int i = 0; i < exponent; i++) {
            result *= base;
            if (result > 1e100) {  // Simple overflow check
                throw OverflowException("Power");
            }
        }
        return result;
    }
};

// Container with exception safety
class SafeVector {
private:
    vector<int> data;
    
public:
    // Strong exception safety
    void push_back_safe(int value) {
        data.push_back(value);  // If this throws, object state unchanged
    }
    
    // Basic exception safety
    void reserve_space(size_t size) {
        try {
            data.reserve(size);
        } catch (const bad_alloc& e) {
            cout << "Memory allocation failed: " << e.what() << endl;
            throw;  // Re-throw to caller
        }
    }
    
    // No-throw guarantee
    size_t size() const noexcept {
        return data.size();
    }
    
    // Strong exception safety with rollback
    void batch_insert(const vector<int>& values) {
        size_t original_size = data.size();
        try {
            for (int value : values) {
                data.push_back(value);
            }
        } catch (...) {
            // Rollback to original state
            data.resize(original_size);
            throw;
        }
    }
    
    void print() const {
        cout << "Vector contents: ";
        for (int val : data) {
            cout << val << " ";
        }
        cout << endl;
    }
};

// Function demonstrating exception specifications (C++11)
void noThrowFunction() noexcept {
    cout << "This function guarantees not to throw" << endl;
    // If an exception is thrown here, std::terminate is called
}

// Modern exception handling patterns
class ModernExceptionExample {
public:
    // Using std::optional instead of exceptions for expected failures
    static optional<double> safe_divide_optional(double a, double b) {
        if (b == 0.0) {
            return nullopt;  // No exception, just empty optional
        }
        return a / b;
    }
    
    // Using expected-like pattern (C++23 will have std::expected)
    struct Result {
        bool success;
        double value;
        string error_message;
        
        static Result success_result(double val) {
            return {true, val, ""};
        }
        
        static Result error_result(const string& msg) {
            return {false, 0.0, msg};
        }
    };
    
    static Result safe_divide_result(double a, double b) {
        if (b == 0.0) {
            return Result::error_result("Division by zero");
        }
        return Result::success_result(a / b);
    }
};

int main() {
    cout << "=== COMPREHENSIVE EXCEPTION HANDLING ===" << endl;
    
    // Basic exception handling with custom hierarchy
    cout << "\n--- Custom Exception Hierarchy ---" << endl;
    try {
        double result = SafeCalculator::divide(10, 0);
        cout << "Result: " << result << endl;
    }
    catch (const DivisionByZeroException& e) {
        cout << "Division by zero: " << e.what() << endl;
    }
    catch (const MathException& e) {
        cout << "Math error: " << e.what() << endl;
    }
    catch (const exception& e) {
        cout << "General exception: " << e.what() << endl;
    }
    
    // Exception safety with RAII
    cout << "\n--- RAII and Exception Safety ---" << endl;
    try {
        FileManager file("test.txt");
        file.write("Hello, World!");
        file.write("This is a test file.");
        
        // Simulate an error
        throw runtime_error("Simulated error after file operations");
    }
    catch (const runtime_error& e) {
        cout << "Runtime error: " << e.what() << endl;
        cout << "Note: File was automatically closed due to RAII" << endl;
    }
    
    // Multiple exception types from single function
    cout << "\n--- Multiple Exception Types ---" << endl;
    vector<pair<double, int>> test_cases = {{2.0, 10}, {0.0, -1}, {2.0, 50}};
    
    for (const auto& [base, exp] : test_cases) {
        try {
            double result = SafeCalculator::power(base, exp);
            cout << base << "^" << exp << " = " << result << endl;
        }
        catch (const DivisionByZeroException& e) {
            cout << "Division by zero in power calculation: " << e.what() << endl;
        }
        catch (const NegativeArgumentException& e) {
            cout << "Negative argument: " << e.what() << endl;
        }
        catch (const OverflowException& e) {
            cout << "Overflow: " << e.what() << endl;
        }
    }
    
    // Exception safety levels
    cout << "\n--- Exception Safety Guarantees ---" << endl;
    SafeVector vec;
    try {
        vec.push_back_safe(1);
        vec.push_back_safe(2);
        vec.push_back_safe(3);
        vec.print();
        
        // This might throw, but will rollback
        vector<int> large_batch(1000000, 42);  // Very large batch
        vec.batch_insert(large_batch);
    }
    catch (const bad_alloc& e) {
        cout << "Memory allocation failed, but vector state preserved" << endl;
        vec.print();  // Should show original content
    }
    catch (...) {
        cout << "Unknown exception caught" << endl;
    }
    
    // Modern alternatives to exceptions
    cout << "\n--- Modern Alternatives ---" << endl;
    
    // Using optional
    if (auto result = ModernExceptionExample::safe_divide_optional(10, 2)) {
        cout << "Division result: " << *result << endl;
    } else {
        cout << "Division failed (returned empty optional)" << endl;
    }
    
    // Using result pattern
    auto result = ModernExceptionExample::safe_divide_result(10, 0);
    if (result.success) {
        cout << "Division result: " << result.value << endl;
    } else {
        cout << "Division failed: " << result.error_message << endl;
    }
    
    // Nested try-catch
    cout << "\n--- Nested Exception Handling ---" << endl;
    try {
        try {
            long long fact = SafeCalculator::factorial(-1);
            cout << "Factorial: " << fact << endl;
        }
        catch (const NegativeArgumentException& e) {
            cout << "Caught negative argument, retrying with positive..." << endl;
            long long fact = SafeCalculator::factorial(5);
            cout << "Factorial of 5: " << fact << endl;
        }
    }
    catch (const exception& e) {
        cout << "Final catch: " << e.what() << endl;
    }
    
    // No-throw function
    noThrowFunction();
    
    cout << "\n=== Exception handling demonstration complete ===" << endl;
    return 0;
}

Exception Handling Best Practices:

Do's

  • Use RAII for automatic resource cleanup
  • Catch exceptions by const reference
  • Create meaningful exception hierarchies
  • Provide strong exception safety guarantees
  • Use noexcept for functions that don't throw
  • Consider alternatives like std::optional

Don'ts

  • Don't use exceptions for normal control flow
  • Don't throw exceptions from destructors
  • Don't catch exceptions you can't handle
  • Don't use raw pointers with exceptions
  • Don't ignore exception safety guarantees
  • Don't use exceptions in performance-critical code

Performance Note: Exceptions have zero cost when not thrown, but throwing and catching exceptions can be expensive. Use them for exceptional conditions, not regular program flow.

Modern Trend: Many modern C++ codebases are moving toward alternatives like std::optional, std::expected (C++23), and error codes for better performance and explicit error handling.

Lambda Expressions & Functional Programming

Lambda expressions provide a concise way to define anonymous functions, forming the foundation of functional programming in C++.

Lambda Expressions

lambda_expressions_example.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

int main() {
    // Basic lambda
    cout << "=== Basic Lambda ===" << endl;
    auto greet = []() {
        cout << "Hello from lambda!" << endl;
    };
    greet();
    
    // Lambda with parameters
    auto add = [](int a, int b) {
        return a + b;
    };
    cout << "5 + 3 = " << add(5, 3) << endl;
    
    // Lambda with capture
    cout << "\n=== Lambda with Capture ===" << endl;
    int multiplier = 10;
    
    // Capture by value
    auto multiplyByValue = [multiplier](int x) {
        return x * multiplier;
    };
    
    // Capture by reference
    auto multiplyByRef = [&multiplier](int x) {
        multiplier++; // Can modify the original variable
        return x * multiplier;
    };
    
    cout << "Multiply 5 by value: " << multiplyByValue(5) << endl;
    cout << "Multiplier before ref call: " << multiplier << endl;
    cout << "Multiply 5 by ref: " << multiplyByRef(5) << endl;
    cout << "Multiplier after ref call: " << multiplier << endl;
    
    // Using lambdas with STL algorithms
    cout << "\n=== Lambda with STL Algorithms ===" << endl;
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Find even numbers
    cout << "Even numbers: ";
    for_each(numbers.begin(), numbers.end(), [](int n) {
        if (n % 2 == 0) {
            cout << n << " ";
        }
    });
    cout << endl;
    
    // Transform elements
    vector<int> squared;
    transform(numbers.begin(), numbers.end(), back_inserter(squared),
              [](int n) { return n * n; });
    
    cout << "Squared numbers: ";
    for (int n : squared) {
        cout << n << " ";
    }
    cout << endl;
    
    // Count elements with condition
    int count = count_if(numbers.begin(), numbers.end(),
                        [](int n) { return n > 5; });
    cout << "Numbers greater than 5: " << count << endl;
    
    // Generic lambda (C++14)
    cout << "\n=== Generic Lambda ===" << endl;
    auto genericAdd = [](auto a, auto b) {
        return a + b;
    };
    
    cout << "Generic add (int): " << genericAdd(5, 3) << endl;
    cout << "Generic add (double): " << genericAdd(5.5, 3.2) << endl;
    cout << "Generic add (string): " << genericAdd(string("Hello "), string("World")) << endl;
    
    return 0;
}

Functional Programming Concepts

Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions, avoiding state and mutable data.

functional_programming.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <optional>
#include <string>
#include <map>
using namespace std;

// ===== HIGHER-ORDER FUNCTIONS =====
// Functions that take other functions as parameters or return functions

// Function that takes a function as parameter
template<typename Func>
void applyToRange(int start, int end, Func func) {
    for (int i = start; i <= end; ++i) {
        func(i);
    }
}

// Function that returns a function
auto createMultiplier(int factor) {
    return [factor](int x) { return x * factor; };
}

// Curry function - transforms function with multiple arguments into chain of functions
auto curry_add = [](int x) {
    return [x](int y) {
        return x + y;
    };
};

// ===== PURE FUNCTIONS =====
// Functions with no side effects - same input always produces same output

int pure_add(int a, int b) {
    return a + b; // No side effects, deterministic
}

// Not pure - has side effects
int counter = 0;
int impure_add(int a, int b) {
    counter++; // Side effect - modifies global state
    return a + b;
}

// ===== IMMUTABLE DATA STRUCTURES =====
class ImmutableVector {
private:
    vector<int> data;
    
public:
    ImmutableVector(const vector<int>& vec) : data(vec) {}
    
    // Returns new vector instead of modifying existing one
    ImmutableVector append(int value) const {
        vector<int> newData = data;
        newData.push_back(value);
        return ImmutableVector(newData);
    }
    
    ImmutableVector filter(function<bool(int)> predicate) const {
        vector<int> result;
        copy_if(data.begin(), data.end(), back_inserter(result), predicate);
        return ImmutableVector(result);
    }
    
    template<typename Func>
    auto map(Func transformer) const {
        vector<decltype(transformer(data[0]))> result;
        transform(data.begin(), data.end(), back_inserter(result), transformer);
        return result;
    }
    
    int reduce(function<int(int, int)> reducer, int initial = 0) const {
        return accumulate(data.begin(), data.end(), initial, reducer);
    }
    
    void print() const {
        for (int val : data) {
            cout << val << " ";
        }
        cout << endl;
    }
    
    size_t size() const { return data.size(); }
    int operator[](size_t index) const { return data[index]; }
};

// ===== MONADS (Optional as example) =====
template<typename T>
class Maybe {
private:
    optional<T> value;
    
public:
    Maybe() : value(nullopt) {}
    Maybe(T val) : value(val) {}
    
    bool hasValue() const { return value.has_value(); }
    T getValue() const { return value.value(); }
    
    // Bind operation (flatMap)
    template<typename Func>
    auto bind(Func func) const {
        if (hasValue()) {
            return func(getValue());
        } else {
            return Maybe<decltype(func(getValue()).getValue())>();
        }
    }
    
    // Map operation
    template<typename Func>
    auto map(Func func) const {
        if (hasValue()) {
            return Maybe<decltype(func(getValue()))>(func(getValue()));
        } else {
            return Maybe<decltype(func(getValue()))>();
        }
    }
};

// Helper function to create Maybe
template<typename T>
Maybe<T> some(T value) {
    return Maybe<T>(value);
}

template<typename T>
Maybe<T> none() {
    return Maybe<T>();
}

// Safe division that returns Maybe
Maybe<double> safeDivide(double a, double b) {
    if (b != 0) {
        return some(a / b);
    } else {
        return none<double>();
    }
}

// ===== FUNCTION COMPOSITION =====
template<typename F, typename G>
auto compose(F f, G g) {
    return [f, g](auto x) {
        return f(g(x));
    };
}

// Pipe operator simulation
template<typename T, typename Func>
auto operator|(T&& value, Func func) {
    return func(forward<T>(value));
}

int main() {
    cout << "=== Functional Programming in C++ ===" << endl;
    
    // ===== HIGHER-ORDER FUNCTIONS =====
    cout << "\n1. Higher-Order Functions:" << endl;
    
    // Function as parameter
    cout << "Numbers 1-5: ";
    applyToRange(1, 5, [](int x) { cout << x << " "; });
    cout << endl;
    
    // Function returning function
    auto multiplyBy3 = createMultiplier(3);
    cout << "5 * 3 = " << multiplyBy3(5) << endl;
    
    // Currying
    auto add5 = curry_add(5);
    cout << "Curried add: 5 + 10 = " << add5(10) << endl;
    
    // ===== MAP, FILTER, REDUCE =====
    cout << "\n2. Map, Filter, Reduce:" << endl;
    
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Map: Transform each element
    vector<int> squares;
    transform(numbers.begin(), numbers.end(), back_inserter(squares),
              [](int x) { return x * x; });
    
    cout << "Squares: ";
    for (int sq : squares) cout << sq << " ";
    cout << endl;
    
    // Filter: Select elements based on condition
    vector<int> evens;
    copy_if(numbers.begin(), numbers.end(), back_inserter(evens),
            [](int x) { return x % 2 == 0; });
    
    cout << "Even numbers: ";
    for (int even : evens) cout << even << " ";
    cout << endl;
    
    // Reduce: Combine all elements into single value
    int sum = accumulate(numbers.begin(), numbers.end(), 0);
    int product = accumulate(numbers.begin(), numbers.end(), 1,
                           [](int acc, int x) { return acc * x; });
    
    cout << "Sum: " << sum << ", Product: " << product << endl;
    
    // ===== IMMUTABLE DATA STRUCTURES =====
    cout << "\n3. Immutable Data Structures:" << endl;
    
    ImmutableVector vec({1, 2, 3, 4, 5});
    cout << "Original vector: ";
    vec.print();
    
    auto vec2 = vec.append(6).append(7);
    cout << "After append (original unchanged): ";
    vec.print();
    cout << "New vector: ";
    vec2.print();
    
    auto evenVec = vec2.filter([](int x) { return x % 2 == 0; });
    cout << "Even numbers: ";
    evenVec.print();
    
    auto doubled = vec.map([](int x) { return x * 2; });
    cout << "Doubled: ";
    for (int val : doubled) cout << val << " ";
    cout << endl;
    
    int totalSum = vec2.reduce([](int acc, int x) { return acc + x; });
    cout << "Sum using reduce: " << totalSum << endl;
    
    // ===== MONADS (Maybe) =====
    cout << "\n4. Monads (Maybe/Optional):" << endl;
    
    auto result1 = safeDivide(10, 2);
    auto result2 = safeDivide(10, 0);
    
    if (result1.hasValue()) {
        cout << "10 / 2 = " << result1.getValue() << endl;
    }
    
    if (!result2.hasValue()) {
        cout << "10 / 0 = undefined (safely handled)" << endl;
    }
    
    // Chaining operations with Maybe
    auto chainedResult = some(20.0)
        .bind([](double x) { return safeDivide(x, 4); })
        .bind([](double x) { return safeDivide(x, 2.5); })
        .map([](double x) { return x * 2; });
    
    if (chainedResult.hasValue()) {
        cout << "Chained operations result: " << chainedResult.getValue() << endl;
    }
    
    // ===== FUNCTION COMPOSITION =====
    cout << "\n5. Function Composition:" << endl;
    
    auto addOne = [](int x) { return x + 1; };
    auto multiplyBy2 = [](int x) { return x * 2; };
    auto toString = [](int x) { return to_string(x); };
    
    // Compose functions
    auto addOneAndDouble = compose(multiplyBy2, addOne);
    cout << "addOneAndDouble(5): " << addOneAndDouble(5) << endl;
    
    // Pipeline-style composition
    string result = 5 
        | addOne 
        | multiplyBy2 
        | toString;
    cout << "Pipeline result: " << result << endl;
    
    // ===== RECURSION vs ITERATION =====
    cout << "\n6. Functional Recursion:" << endl;
    
    // Functional factorial
    function<int(int)> factorial = [&factorial](int n) {
        return (n <= 1) ? 1 : n * factorial(n - 1);
    };
    
    cout << "Factorial of 5: " << factorial(5) << endl;
    
    // Tail-recursive version (more efficient)
    function<int(int, int)> tailFactorial = [&tailFactorial](int n, int acc) {
        return (n <= 1) ? acc : tailFactorial(n - 1, n * acc);
    };
    
    cout << "Tail recursive factorial of 5: " << tailFactorial(5, 1) << endl;
    
    return 0;
}

Functional Programming Principles

Immutability

Data structures that cannot be modified after creation, reducing bugs and enabling safe concurrent programming.

Pure Functions

Functions with no side effects that always return the same output for the same input, making code predictable and testable.

Higher-Order Functions

Functions that accept other functions as parameters or return functions, enabling powerful abstraction patterns.

Function Composition

Combining simple functions to build more complex operations, promoting code reuse and modularity.

Recursion

Solving problems by breaking them into smaller subproblems, often more elegant than iterative solutions.

Monads

Design patterns for handling computations with context (like null values, errors) in a composable way.

Common Functional Algorithms

Map

Transform each element in a collection using a function

transform(begin, end, result, function)

Filter

Select elements from a collection based on a predicate

copy_if(begin, end, result, predicate)

Reduce/Fold

Combine all elements into a single value using an accumulator

accumulate(begin, end, initial, binary_op)

Zip

Combine elements from multiple collections pairwise

transform(begin1, end1, begin2, result, binary_op)

Functional Programming Best Practices:

  • Prefer Pure Functions: Write functions without side effects when possible
  • Use Immutable Data: Create new objects instead of modifying existing ones
  • Compose Functions: Build complex operations from simple, reusable functions
  • Avoid Shared Mutable State: Minimize global variables and shared data
  • Use STL Algorithms: Leverage existing functional algorithms in <algorithm>
  • Consider Performance: Be aware of the overhead of functional approaches
  • Mix Paradigms: Combine functional and object-oriented approaches as appropriate

Move Semantics & Perfect Forwarding

Move semantics allow efficient transfer of resources from temporary objects, while perfect forwarding preserves the value category of function arguments.

Move Semantics

move_semantics_example.cpp
#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <cstring>
using namespace std;

class MyString {
private:
    char* data;
    size_t length;
    
public:
    // Constructor
    MyString(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
        cout << "Constructor: " << data << endl;
    }
    
    // Copy constructor
    MyString(const MyString& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
        cout << "Copy constructor: " << data << endl;
    }
    
    // Move constructor
    MyString(MyString&& other) noexcept {
        data = other.data;
        length = other.length;
        other.data = nullptr;
        other.length = 0;
        cout << "Move constructor: " << data << endl;
    }
    
    // Copy assignment operator
    MyString& operator=(const MyString& other) {
        if (this != &other) {
            delete[] data;
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
            cout << "Copy assignment: " << data << endl;
        }
        return *this;
    }
    
    // Move assignment operator
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            length = other.length;
            other.data = nullptr;
            other.length = 0;
            cout << "Move assignment: " << data << endl;
        }
        return *this;
    }
    
    // Destructor
    ~MyString() {
        if (data) {
            cout << "Destructor: " << data << endl;
            delete[] data;
        } else {
            cout << "Destructor: (moved object)" << endl;
        }
    }
    
    const char* c_str() const { return data ? data : ""; }
};

MyString createString(const char* str) {
    return MyString(str); // Return by value - move constructor called
}

int main() {
    cout << "=== Move Semantics Demo ===" << endl;
    
    // Regular construction
    MyString str1("Hello");
    
    // Copy construction
    MyString str2 = str1;
    
    // Move construction
    MyString str3 = move(str1); // str1 is now in moved-from state
    
    cout << "str1 after move: '" << str1.c_str() << "'" << endl;
    cout << "str3 after move: '" << str3.c_str() << "'" << endl;
    
    // Function returning by value (move optimization)
    cout << "\n=== Function Return ===" << endl;
    MyString str4 = createString("World");
    
    // Using std::move with containers
    cout << "\n=== Container Move ===" << endl;
    vector<MyString> vec;
    vec.push_back(MyString("First"));  // Move constructor
    vec.push_back(MyString("Second")); // Move constructor
    
    MyString str5("Third");
    vec.push_back(move(str5)); // Explicit move
    
    cout << "str5 after move to vector: '" << str5.c_str() << "'" << endl;
    
    return 0;
}

Perfect Forwarding

Perfect forwarding allows template functions to pass arguments to another function while preserving their value category (lvalue or rvalue). This is essential for writing efficient generic code.

perfect_forwarding.cpp
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <memory>
using namespace std;

// ===== DEMONSTRATION CLASS =====
class Resource {
private:
    string name;
    
public:
    // Constructor
    Resource(const string& n) : name(n) {
        cout << "Resource created: " << name << endl;
    }
    
    // Copy constructor
    Resource(const Resource& other) : name(other.name + "_copy") {
        cout << "Resource copied: " << name << endl;
    }
    
    // Move constructor
    Resource(Resource&& other) noexcept : name(move(other.name)) {
        cout << "Resource moved: " << name << endl;
        other.name = "moved_from";
    }
    
    // Destructor
    ~Resource() {
        cout << "Resource destroyed: " << name << endl;
    }
    
    const string& getName() const { return name; }
};

// ===== WITHOUT PERFECT FORWARDING (PROBLEMATIC) =====
void processResource(const Resource& res) {
    cout << "Processing const Resource: " << res.getName() << endl;
}

void processResource(Resource&& res) {
    cout << "Processing moved Resource: " << res.getName() << endl;
}

// Bad wrapper - doesn't preserve value category
template<typename T>
void badWrapper(T arg) {  // Always copies!
    processResource(arg);
}

// Better wrapper but still not perfect
template<typename T>
void betterWrapper(T& arg) {  // Only works with lvalues
    processResource(arg);
}

// ===== PERFECT FORWARDING SOLUTION =====
template<typename T>
void perfectWrapper(T&& arg) {  // Universal/Forwarding reference
    processResource(forward<T>(arg));  // Perfect forwarding!
}

// ===== UNIVERSAL REFERENCES IN ACTION =====
template<typename T>
void analyzeType(T&& param) {
    cout << "Parameter type analysis:" << endl;
    
    if constexpr (is_lvalue_reference_v<T>) {
        cout << "  - T is lvalue reference" << endl;
        cout << "  - param is lvalue" << endl;
    } else {
        cout << "  - T is rvalue reference or value type" << endl;
        cout << "  - param is rvalue" << endl;
    }
}

// ===== PERFECT FORWARDING IN CONSTRUCTORS =====
template<typename T>
class Wrapper {
private:
    T wrapped;
    
public:
    // Perfect forwarding constructor
    template<typename U>
    Wrapper(U&& value) : wrapped(forward<U>(value)) {
        cout << "Wrapper constructed with perfect forwarding" << endl;
    }
    
    T& get() { return wrapped; }
    const T& get() const { return wrapped; }
};

// ===== FACTORY FUNCTION WITH PERFECT FORWARDING =====
template<typename T, typename... Args>
unique_ptr<T> make_unique_perfect(Args&&... args) {
    return unique_ptr<T>(new T(forward<Args>(args)...));
}

// Class for factory demonstration
class ComplexObject {
private:
    string name;
    int value;
    Resource resource;
    
public:
    ComplexObject(const string& n, int v, Resource r) 
        : name(n), value(v), resource(move(r)) {
        cout << "ComplexObject created: " << name << endl;
    }
    
    ~ComplexObject() {
        cout << "ComplexObject destroyed: " << name << endl;
    }
    
    void display() const {
        cout << "ComplexObject: " << name << ", value: " << value 
             << ", resource: " << resource.getName() << endl;
    }
};

// ===== VARIADIC TEMPLATE WITH PERFECT FORWARDING =====
template<typename Func, typename... Args>
auto invokeFunction(Func&& func, Args&&... args) 
    -> decltype(func(forward<Args>(args)...)) {
    cout << "Invoking function with " << sizeof...(args) << " arguments" << endl;
    return func(forward<Args>(args)...);
}

// Function to be invoked
int multiply(int a, int b, int c) {
    return a * b * c;
}

string concatenate(const string& a, const string& b, const string& c) {
    return a + " " + b + " " + c;
}

int main() {
    cout << "=== Perfect Forwarding Demonstration ===" << endl;
    
    // ===== BASIC PERFECT FORWARDING =====
    cout << "\n1. Basic Perfect Forwarding:" << endl;
    
    Resource res1("original");
    
    cout << "\nUsing bad wrapper (always copies):" << endl;
    badWrapper(res1);  // Copies even though we pass lvalue
    
    cout << "\nUsing perfect wrapper:" << endl;
    perfectWrapper(res1);  // Correctly identifies as lvalue
    perfectWrapper(Resource("temporary"));  // Correctly identifies as rvalue
    
    // ===== TYPE ANALYSIS =====
    cout << "\n2. Type Analysis with Universal References:" << endl;
    
    Resource res2("analysis_test");
    cout << "\nPassing lvalue:" << endl;
    analyzeType(res2);
    
    cout << "\nPassing rvalue:" << endl;
    analyzeType(Resource("temp_for_analysis"));
    
    // ===== PERFECT FORWARDING IN CONSTRUCTORS =====
    cout << "\n3. Perfect Forwarding in Constructors:" << endl;
    
    Resource res3("for_wrapper");
    cout << "\nCreating wrapper with lvalue:" << endl;
    Wrapper<Resource> wrapper1(res3);  // Should copy
    
    cout << "\nCreating wrapper with rvalue:" << endl;
    Wrapper<Resource> wrapper2(Resource("for_wrapper_move"));  // Should move
    
    // ===== FACTORY FUNCTION =====
    cout << "\n4. Factory Function with Perfect Forwarding:" << endl;
    
    Resource factoryRes("factory_resource");
    cout << "\nCreating ComplexObject via factory:" << endl;
    auto complexObj = make_unique_perfect<ComplexObject>(
        "MyObject", 
        42, 
        move(factoryRes)  // Perfect forwarding preserves move
    );
    
    complexObj->display();
    
    // ===== VARIADIC TEMPLATES WITH PERFECT FORWARDING =====
    cout << "\n5. Variadic Templates with Perfect Forwarding:" << endl;
    
    // Invoke function with different argument types
    int a = 2, b = 3, c = 4;
    int result1 = invokeFunction(multiply, a, b, c);  // lvalues
    cout << "Multiply result: " << result1 << endl;
    
    string result2 = invokeFunction(concatenate, 
        string("Hello"), 
        string("Perfect"), 
        string("Forwarding"));  // rvalues
    cout << "Concatenate result: " << result2 << endl;
    
    // ===== PERFECT FORWARDING WITH LAMBDAS =====
    cout << "\n6. Perfect Forwarding with Lambdas:" << endl;
    
    auto perfectLambda = [](auto&& func, auto&&... args) {
        cout << "Lambda perfect forwarding" << endl;
        return func(forward<decltype(args)>(args)...);
    };
    
    int lambdaResult = perfectLambda(
        [](int x, int y) { return x + y; }, 
        10, 
        20
    );
    cout << "Lambda result: " << lambdaResult << endl;
    
    // ===== REFERENCE COLLAPSING DEMONSTRATION =====
    cout << "\n7. Reference Collapsing Rules:" << endl;
    cout << "T&  + &   = T&   (lvalue ref)" << endl;
    cout << "T&  + &&  = T&   (lvalue ref)" << endl;
    cout << "T&& + &   = T&   (lvalue ref)" << endl;
    cout << "T&& + &&  = T&&  (rvalue ref)" << endl;
    
    return 0;
}

Key Perfect Forwarding Concepts

Universal References

Template parameters with && that can bind to both lvalues and rvalues, enabling perfect forwarding.

std::forward

Conditionally casts arguments to preserve their original value category when forwarding.

Reference Collapsing

Rules that determine the final reference type when combining multiple reference types.

Template Argument Deduction

Compiler automatically deduces template types based on the arguments passed to the function.

Factory Functions

Functions that create objects while perfectly forwarding constructor arguments.

Variadic Templates

Templates that accept variable numbers of arguments, often used with perfect forwarding.

Perfect Forwarding Best Practices:

  • Use Universal References: Template parameter T&& for forwarding references
  • Always Use std::forward: Don't forward without std::forward<T>
  • Understand Reference Collapsing: Know how && + & = & works
  • Template Constructors: Use perfect forwarding in constructors for efficiency
  • Factory Functions: Perfect forwarding essential for make_unique, emplace_back, etc.
  • Combine with Move Semantics: Perfect forwarding and move semantics work together
  • Watch Out for Overloads: Perfect forwarding can interfere with overload resolution

Modern C++ Features

Modern C++ (C++11/14/17/20) introduces many powerful features.

modern_cpp_features.cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <optional>
#include <variant>
#include <any>
#include <tuple>
using namespace std;

int main() {
    // Auto keyword
    cout << "=== Auto Keyword ===" << endl;
    auto x = 42;        // int
    auto y = 3.14;      // double
    auto z = "Hello";   // const char*
    cout << "x: " << x << ", y: " << y << ", z: " << z << endl;
    
    // Range-based for loops
    cout << "\n=== Range-based For Loop ===" << endl;
    vector<int> numbers = {1, 2, 3, 4, 5};
    for (const auto& num : numbers) {
        cout << num << " ";
    }
    cout << endl;
    
    // Initializer lists
    cout << "\n=== Initializer Lists ===" << endl;
    vector<string> names{"Alice", "Bob", "Charlie"};
    map<string, int> ages{{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};
    
    for (const auto& [name, age] : ages) { // Structured binding (C++17)
        cout << name << " is " << age << " years old" << endl;
    }
    
    // nullptr
    cout << "\n=== nullptr ===" << endl;
    int* ptr = nullptr;
    if (ptr == nullptr) {
        cout << "Pointer is null" << endl;
    }
    
    // Optional (C++17)
    cout << "\n=== std::optional ===" << endl;
    auto findValue = [](const vector<int>& vec, int target) -> optional<size_t> {
        for (size_t i = 0; i < vec.size(); ++i) {
            if (vec[i] == target) {
                return i;
            }
        }
        return nullopt;
    };
    
    if (auto index = findValue(numbers, 3)) {
        cout << "Found 3 at index: " << *index << endl;
    } else {
        cout << "Value not found" << endl;
    }
    
    // Variant (C++17)
    cout << "\n=== std::variant ===" << endl;
    variant<int, string, double> var;
    var = 42;
    cout << "Variant holds int: " << get<int>(var) << endl;
    
    var = "Hello World";
    cout << "Variant holds string: " << get<string>(var) << endl;
    
    // Tuple and structured binding
    cout << "\n=== Tuple and Structured Binding ===" << endl;
    auto person = make_tuple("John", 28, 75000.0);
    auto [name, age, salary] = person;
    cout << "Name: " << name << ", Age: " << age << ", Salary: $" << salary << endl;
    
    // Constexpr
    cout << "\n=== constexpr ===" << endl;
    constexpr auto factorial = [](int n) {
        return (n <= 1) ? 1 : n * factorial(n - 1);
    };
    
    constexpr int fact5 = factorial(5); // Computed at compile time
    cout << "5! = " << fact5 << endl;
    
    // if constexpr (C++17)
    cout << "\n=== if constexpr ===" << endl;
    auto processValue = []<typename T>(T value) {
        if constexpr (is_integral_v<T>) {
            cout << "Processing integer: " << value << endl;
        } else if constexpr (is_floating_point_v<T>) {
            cout << "Processing floating point: " << value << endl;
        } else {
            cout << "Processing other type" << endl;
        }
    };
    
    processValue(42);
    processValue(3.14);
    processValue("Hello");
    
    return 0;
}

Multithreading & Concurrency

C++11 introduced built-in support for multithreading and concurrent programming.

Multithreading Basics

Multithreading allows programs to execute multiple threads concurrently, improving performance on multi-core systems.

multithreading_example.cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <vector>
#include <chrono>
using namespace std;

// Global variables for demonstration
mutex mtx;
condition_variable cv;
bool ready = false;
int counter = 0;

// Simple thread function
void printNumbers(int start, int end, const string& threadName) {
    for (int i = start; i <= end; ++i) {
        {
            lock_guard<mutex> lock(mtx);
            cout << threadName << ": " << i << endl;
        }
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

// Thread-safe counter increment
void incrementCounter(int times) {
    for (int i = 0; i < times; ++i) {
        lock_guard<mutex> lock(mtx);
        ++counter;
    }
}

// Producer-Consumer example
void producer() {
    this_thread::sleep_for(chrono::seconds(1));
    {
        lock_guard<mutex> lock(mtx);
        ready = true;
        cout << "Producer: Data is ready!" << endl;
    }
    cv.notify_one();
}

void consumer() {
    unique_lock<mutex> lock(mtx);
    cv.wait(lock, [] { return ready; });
    cout << "Consumer: Processing data..." << endl;
}

// Function for async/future example
int calculateSum(int start, int end) {
    int sum = 0;
    for (int i = start; i <= end; ++i) {
        sum += i;
    }
    this_thread::sleep_for(chrono::milliseconds(500)); // Simulate work
    return sum;
}

int main() {
    // Basic thread creation and joining
    cout << "=== Basic Threading ===" << endl;
    thread t1(printNumbers, 1, 5, "Thread1");
    thread t2(printNumbers, 6, 10, "Thread2");
    
    t1.join();
    t2.join();
    
    // Thread-safe operations
    cout << "\n=== Thread-Safe Counter ===" << endl;
    counter = 0; // Reset counter
    
    vector<thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(incrementCounter, 1000);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    cout << "Final counter value: " << counter << endl;
    
    // Producer-Consumer pattern
    cout << "\n=== Producer-Consumer ===" << endl;
    ready = false; // Reset
    
    thread producerThread(producer);
    thread consumerThread(consumer);
    
    producerThread.join();
    consumerThread.join();
    
    // Async and Future
    cout << "\n=== Async and Future ===" << endl;
    
    // Launch async tasks
    auto future1 = async(launch::async, calculateSum, 1, 1000);
    auto future2 = async(launch::async, calculateSum, 1001, 2000);
    auto future3 = async(launch::async, calculateSum, 2001, 3000);
    
    cout << "Calculating sums asynchronously..." << endl;
    
    // Get results
    int sum1 = future1.get();
    int sum2 = future2.get();
    int sum3 = future3.get();
    
    cout << "Sum 1-1000: " << sum1 << endl;
    cout << "Sum 1001-2000: " << sum2 << endl;
    cout << "Sum 2001-3000: " << sum3 << endl;
    cout << "Total sum: " << (sum1 + sum2 + sum3) << endl;
    
    // Thread with lambda
    cout << "\n=== Thread with Lambda ===" << endl;
    thread lambdaThread([]() {
        for (int i = 0; i < 3; ++i) {
            cout << "Lambda thread: " << i << endl;
            this_thread::sleep_for(chrono::milliseconds(200));
        }
    });
    
    lambdaThread.join();
    
    cout << "All threads completed!" << endl;
    return 0;
}

Concurrency Concepts

Concurrency is about dealing with multiple tasks at once, while parallelism is about executing multiple tasks simultaneously. C++ provides various tools for concurrent programming.

concurrency_patterns.cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <queue>
#include <memory>
#include <future>
#include <chrono>
#include <random>
using namespace std;

// ===== ATOMIC OPERATIONS =====
atomic<int> atomicCounter{0};
int regularCounter = 0;
mutex counterMutex;

void atomicIncrement(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        atomicCounter.fetch_add(1, memory_order_relaxed);
    }
}

void regularIncrement(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        lock_guard<mutex> lock(counterMutex);
        ++regularCounter;
    }
}

// ===== THREAD-SAFE QUEUE =====
template<typename T>
class ThreadSafeQueue {
private:
    queue<T> queue_;
    mutable mutex mutex_;
    condition_variable condition_;

public:
    void push(T item) {
        lock_guard<mutex> lock(mutex_);
        queue_.push(item);
        condition_.notify_one();
    }

    bool tryPop(T& item) {
        lock_guard<mutex> lock(mutex_);
        if (queue_.empty()) {
            return false;
        }
        item = queue_.front();
        queue_.pop();
        return true;
    }

    void waitAndPop(T& item) {
        unique_lock<mutex> lock(mutex_);
        condition_.wait(lock, [this] { return !queue_.empty(); });
        item = queue_.front();
        queue_.pop();
    }

    bool empty() const {
        lock_guard<mutex> lock(mutex_);
        return queue_.empty();
    }
};

// ===== WORKER THREAD POOL SIMULATION =====
class SimpleTask {
public:
    int id;
    string description;
    
    SimpleTask(int id, const string& desc) : id(id), description(desc) {}
    
    void execute() {
        cout << "Executing task " << id << ": " << description << endl;
        // Simulate work
        this_thread::sleep_for(chrono::milliseconds(100 + rand() % 200));
        cout << "Task " << id << " completed!" << endl;
    }
};

ThreadSafeQueue<shared_ptr<SimpleTask>> taskQueue;
atomic<bool> stopWorkers{false};

void worker(int workerId) {
    cout << "Worker " << workerId << " started" << endl;
    
    while (!stopWorkers) {
        shared_ptr<SimpleTask> task;
        if (taskQueue.tryPop(task)) {
            cout << "Worker " << workerId << " picked up task" << endl;
            task->execute();
        } else {
            this_thread::sleep_for(chrono::milliseconds(10));
        }
    }
    
    cout << "Worker " << workerId << " stopped" << endl;
}

// ===== LOCK-FREE PROGRAMMING =====
class LockFreeCounter {
private:
    atomic<int> count{0};
    
public:
    void increment() {
        count.fetch_add(1, memory_order_relaxed);
    }
    
    void decrement() {
        count.fetch_sub(1, memory_order_relaxed);
    }
    
    int get() const {
        return count.load(memory_order_relaxed);
    }
    
    // Compare and swap operation
    bool compareAndSwap(int expected, int desired) {
        return count.compare_exchange_weak(expected, desired, memory_order_relaxed);
    }
};

// ===== SHARED STATE WITH READER-WRITER LOCKS =====
class SharedResource {
private:
    mutable shared_mutex rwMutex;
    vector<int> data;
    
public:
    void write(int value) {
        unique_lock<shared_mutex> lock(rwMutex);
        data.push_back(value);
        cout << "Written: " << value << " (size: " << data.size() << ")" << endl;
    }
    
    vector<int> read() const {
        shared_lock<shared_mutex> lock(rwMutex);
        cout << "Reading data (size: " << data.size() << ")" << endl;
        return data;
    }
    
    size_t size() const {
        shared_lock<shared_mutex> lock(rwMutex);
        return data.size();
    }
};

int main() {
    cout << "=== Concurrency Patterns Demo ===" << endl;
    
    // ===== ATOMIC OPERATIONS DEMO =====
    cout << "\n1. Atomic vs Regular Operations:" << endl;
    
    const int iterations = 10000;
    const int numThreads = 4;
    
    // Test atomic operations
    vector<thread> atomicThreads;
    auto start = chrono::high_resolution_clock::now();
    
    for (int i = 0; i < numThreads; ++i) {
        atomicThreads.emplace_back(atomicIncrement, iterations);
    }
    
    for (auto& t : atomicThreads) {
        t.join();
    }
    
    auto atomicDuration = chrono::high_resolution_clock::now() - start;
    cout << "Atomic counter result: " << atomicCounter.load() << endl;
    
    // Test regular operations with mutex
    vector<thread> regularThreads;
    start = chrono::high_resolution_clock::now();
    
    for (int i = 0; i < numThreads; ++i) {
        regularThreads.emplace_back(regularIncrement, iterations);
    }
    
    for (auto& t : regularThreads) {
        t.join();
    }
    
    auto regularDuration = chrono::high_resolution_clock::now() - start;
    cout << "Regular counter result: " << regularCounter << endl;
    
    // ===== WORKER THREAD POOL DEMO =====
    cout << "\n2. Worker Thread Pool:" << endl;
    
    // Start worker threads
    vector<thread> workers;
    for (int i = 0; i < 3; ++i) {
        workers.emplace_back(worker, i + 1);
    }
    
    // Add tasks to queue
    for (int i = 1; i <= 8; ++i) {
        auto task = make_shared<SimpleTask>(i, "Process data batch " + to_string(i));
        taskQueue.push(task);
    }
    
    // Let workers process tasks
    this_thread::sleep_for(chrono::seconds(3));
    
    // Stop workers
    stopWorkers = true;
    for (auto& w : workers) {
        w.join();
    }
    
    // ===== LOCK-FREE PROGRAMMING DEMO =====
    cout << "\n3. Lock-Free Programming:" << endl;
    
    LockFreeCounter lockFreeCounter;
    
    vector<thread> lockFreeThreads;
    for (int i = 0; i < 4; ++i) {
        lockFreeThreads.emplace_back([&lockFreeCounter]() {
            for (int j = 0; j < 1000; ++j) {
                lockFreeCounter.increment();
            }
        });
    }
    
    for (auto& t : lockFreeThreads) {
        t.join();
    }
    
    cout << "Lock-free counter final value: " << lockFreeCounter.get() << endl;
    
    // ===== READER-WRITER DEMO =====
    cout << "\n4. Reader-Writer Shared Resource:" << endl;
    
    SharedResource sharedResource;
    
    // Writer threads
    vector<thread> writers;
    for (int i = 0; i < 2; ++i) {
        writers.emplace_back([&sharedResource, i]() {
            for (int j = 1; j <= 5; ++j) {
                sharedResource.write(i * 10 + j);
                this_thread::sleep_for(chrono::milliseconds(50));
            }
        });
    }
    
    // Reader threads
    vector<thread> readers;
    for (int i = 0; i < 3; ++i) {
        readers.emplace_back([&sharedResource, i]() {
            for (int j = 0; j < 3; ++j) {
                auto data = sharedResource.read();
                this_thread::sleep_for(chrono::milliseconds(30));
            }
        });
    }
    
    // Wait for all threads
    for (auto& w : writers) {
        w.join();
    }
    for (auto& r : readers) {
        r.join();
    }
    
    cout << "\n=== Concurrency Demo Completed ===" << endl;
    cout << "Final shared resource size: " << sharedResource.size() << endl;
    
    return 0;
}

Key Concurrency Concepts:

Atomic Operations

Lock-free operations that are guaranteed to be performed atomically without race conditions.

Thread Pools

Pre-created threads that wait for tasks, improving performance by avoiding thread creation overhead.

Shared Memory

Memory accessible by multiple threads, requiring synchronization to prevent data races.

Lock-Free Programming

Programming without traditional locks, using atomic operations and memory ordering.

Reader-Writer Locks

Allow multiple readers or single writer access to shared resources for better performance.

Memory Ordering

Control over how memory operations are ordered across threads for correctness and performance.

Concurrency Best Practices:

  • Minimize Shared State: Reduce the amount of data shared between threads
  • Use Immutable Data: Prefer immutable objects that can be safely shared
  • Avoid Data Races: Always synchronize access to shared mutable data
  • Choose Right Synchronization: Use appropriate synchronization primitives for your use case
  • Test Thoroughly: Concurrent bugs can be difficult to reproduce and debug
  • Consider Lock-Free: For high-performance scenarios, consider lock-free algorithms

Template Metaprogramming

Template metaprogramming is a technique where templates are used to generate code at compile-time. It allows you to perform computations, make decisions, and generate specialized code during compilation, resulting in highly optimized runtime performance with zero runtime overhead.

Core Concepts:

🕰️ Compile-time Computation

Calculations performed during compilation, not runtime

🎯 Template Specialization

Different implementations for specific types or values

🔄 Recursive Templates

Templates that call themselves with modified parameters

✨ SFINAE

Substitution Failure Is Not An Error - conditional compilation

🎨 Type Traits

Compile-time type information and manipulation

🚀 constexpr

Modern compile-time evaluation with cleaner syntax

Classical Template Metaprogramming:

classical_metaprogramming.cpp
#include <iostream>
#include <type_traits>
#include <chrono>
#include <limits>

// ===== COMPILE-TIME FACTORIAL =====
template<int N>
struct Factorial {
    static constexpr long long value = N * Factorial<N - 1>::value;
};

// Template specialization for base case
template<>
struct Factorial<0> {
    static constexpr long long value = 1;
};

template<>
struct Factorial<1> {
    static constexpr long long value = 1;
};

// ===== COMPILE-TIME FIBONACCI =====
template<int N>
struct Fibonacci {
    static constexpr long long value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

template<>
struct Fibonacci<0> {
    static constexpr long long value = 0;
};

template<>
struct Fibonacci<1> {
    static constexpr long long value = 1;
};

// ===== COMPILE-TIME PRIME CHECKING =====
template<int N, int Divisor = N-1>
struct IsPrime {
    static constexpr bool value = (N % Divisor != 0) && IsPrime<N, Divisor-1>::value;
};

template<int N>
struct IsPrime<N, 1> {
    static constexpr bool value = true;
};

template<>
struct IsPrime<1, 0> {
    static constexpr bool value = false;
};

template<>
struct IsPrime<2, 1> {
    static constexpr bool value = true;
};

// ===== COMPILE-TIME POWER =====
template<int Base, int Exponent>
struct Power {
    static constexpr long long value = Base * Power<Base, Exponent-1>::value;
};

template<int Base>
struct Power<Base, 0> {
    static constexpr long long value = 1;
};

// ===== TYPE LIST MANIPULATION =====
// Basic type list
template<typename... Types>
struct TypeList {};

// Get length of type list
template<typename List>
struct Length;

template<typename... Types>
struct Length<TypeList<Types...>> {
    static constexpr size_t value = sizeof...(Types);
};

// Get type at index
template<size_t Index, typename List>
struct TypeAt;

template<size_t Index, typename Head, typename... Tail>
struct TypeAt<Index, TypeList<Head, Tail...>> {
    using type = typename TypeAt<Index-1, TypeList<Tail...>>::type;
};

template<typename Head, typename... Tail>
struct TypeAt<0, TypeList<Head, Tail...>> {
    using type = Head;
};

// ===== COMPILE-TIME STRING PROCESSING =====
template<char... Chars>
struct String {
    static constexpr size_t length = sizeof...(Chars);
    static constexpr char data[length + 1] = {Chars..., '\0'};
};

template<char... Chars>
constexpr char String<Chars...>::data[String<Chars...>::length + 1];

// User-defined literal for compile-time strings
template<typename CharT, CharT... Chars>
constexpr String<Chars...> operator""_s() {
    return {};
}

int main() {
    std::cout << "=== CLASSICAL TEMPLATE METAPROGRAMMING ===" << std::endl;
    
    // Compile-time calculations
    constexpr auto fact5 = Factorial<5>::value;
    constexpr auto fact10 = Factorial<10>::value;
    constexpr auto fib10 = Fibonacci<10>::value;
    constexpr auto fib20 = Fibonacci<20>::value;
    
    std::cout << "\n--- Compile-time Calculations ---" << std::endl;
    std::cout << "5! = " << fact5 << std::endl;
    std::cout << "10! = " << fact10 << std::endl;
    std::cout << "Fibonacci(10) = " << fib10 << std::endl;
    std::cout << "Fibonacci(20) = " << fib20 << std::endl;
    
    // Prime checking
    std::cout << "\n--- Prime Checking ---" << std::endl;
    std::cout << "Is 17 prime? " << std::boolalpha << IsPrime<17>::value << std::endl;
    std::cout << "Is 18 prime? " << std::boolalpha << IsPrime<18>::value << std::endl;
    std::cout << "Is 97 prime? " << std::boolalpha << IsPrime<97>::value << std::endl;
    
    // Power calculations
    constexpr auto pow2_10 = Power<2, 10>::value;
    constexpr auto pow3_5 = Power<3, 5>::value;
    
    std::cout << "\n--- Power Calculations ---" << std::endl;
    std::cout << "2^10 = " << pow2_10 << std::endl;
    std::cout << "3^5 = " << pow3_5 << std::endl;
    
    // Type list operations
    using MyTypes = TypeList<int, double, std::string, char>;
    constexpr auto length = Length<MyTypes>::value;
    using SecondType = TypeAt<1, MyTypes>::type;  // double
    
    std::cout << "\n--- Type List Operations ---" << std::endl;
    std::cout << "Type list length: " << length << std::endl;
    std::cout << "Second type size: " << sizeof(SecondType) << " bytes" << std::endl;
    
    return 0;
}

Modern constexpr Approach (C++11/14/17):

modern_constexpr.cpp
#include <iostream>
#include <array>
#include <algorithm>
#include <numeric>
#include <string_view>

// ===== MODERN CONSTEXPR FUNCTIONS =====

// Factorial using constexpr function (much cleaner than template recursion)
constexpr long long factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

// Fibonacci with memoization at compile-time
constexpr long long fibonacci(int n) {
    if (n <= 1) return n;
    
    long long a = 0, b = 1;
    for (int i = 2; i <= n; ++i) {
        long long temp = a + b;
        a = b;
        b = temp;
    }
    return b;
}

// Prime checking with optimized algorithm
constexpr bool isPrime(int n) {
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) return false;
    }
    return true;
}

// Generate array of first N primes at compile-time
template<size_t N>
constexpr std::array<int, N> generatePrimes() {
    std::array<int, N> primes{};
    size_t count = 0;
    int candidate = 2;
    
    while (count < N) {
        if (isPrime(candidate)) {
            primes[count] = candidate;
            ++count;
        }
        ++candidate;
    }
    return primes;
}

// Compile-time string hashing
constexpr size_t hashString(std::string_view str) {
    size_t hash = 5381;
    for (char c : str) {
        hash = ((hash << 5) + hash) + static_cast<size_t>(c);
    }
    return hash;
}

// Compile-time mathematical operations on arrays
template<size_t N>
constexpr std::array<int, N> generateSquares() {
    std::array<int, N> squares{};
    for (size_t i = 0; i < N; ++i) {
        squares[i] = static_cast<int>(i * i);
    }
    return squares;
}

// Compile-time sorting
template<size_t N>
constexpr std::array<int, N> bubbleSort(std::array<int, N> arr) {
    for (size_t i = 0; i < N - 1; ++i) {
        for (size_t j = 0; j < N - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

// ===== COMPILE-TIME DECISION MAKING =====

// if constexpr examples (C++17)
template<typename T>
constexpr auto processValue(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value * 2;  // Double integers
    } else if constexpr (std::is_floating_point_v<T>) {
        return value * 1.5;  // Multiply floats by 1.5
    } else {
        return value;  // Return as-is for other types
    }
}

// Compile-time polymorphism with concepts (C++20)
#if __cplusplus >= 202002L
#include <concepts>

template<std::integral T>
constexpr T power(T base, unsigned int exp) {
    T result = 1;
    for (unsigned int i = 0; i < exp; ++i) {
        result *= base;
    }
    return result;
}

template<std::floating_point T>
constexpr T power(T base, unsigned int exp) {
    T result = 1.0;
    for (unsigned int i = 0; i < exp; ++i) {
        result *= base;
    }
    return result;
}
#endif

int main() {
    std::cout << "=== MODERN CONSTEXPR METAPROGRAMMING ===" << std::endl;
    
    // Compile-time calculations with constexpr functions
    constexpr auto fact5 = factorial(5);
    constexpr auto fact10 = factorial(10);
    constexpr auto fib15 = fibonacci(15);
    constexpr auto fib25 = fibonacci(25);
    
    std::cout << "\n--- constexpr Function Results ---" << std::endl;
    std::cout << "5! = " << fact5 << std::endl;
    std::cout << "10! = " << fact10 << std::endl;
    std::cout << "Fibonacci(15) = " << fib15 << std::endl;
    std::cout << "Fibonacci(25) = " << fib25 << std::endl;
    
    // Compile-time prime generation
    constexpr auto first10Primes = generatePrimes<10>();
    std::cout << "\n--- First 10 Primes (compile-time generated) ---" << std::endl;
    for (size_t i = 0; i < first10Primes.size(); ++i) {
        std::cout << first10Primes[i];
        if (i < first10Primes.size() - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    
    // Compile-time string hashing
    constexpr auto hash1 = hashString("Hello, World!");
    constexpr auto hash2 = hashString("Template Metaprogramming");
    
    std::cout << "\n--- Compile-time String Hashing ---" << std::endl;
    std::cout << "Hash of 'Hello, World!': " << hash1 << std::endl;
    std::cout << "Hash of 'Template Metaprogramming': " << hash2 << std::endl;
    
    // Compile-time array operations
    constexpr auto squares = generateSquares<10>();
    constexpr auto unsorted = std::array{5, 2, 8, 1, 9, 3};
    constexpr auto sorted = bubbleSort(unsorted);
    
    std::cout << "\n--- Compile-time Array Operations ---" << std::endl;
    std::cout << "Squares of 0-9: ";
    for (size_t i = 0; i < squares.size(); ++i) {
        std::cout << squares[i];
        if (i < squares.size() - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    
    std::cout << "Sorted array: ";
    for (size_t i = 0; i < sorted.size(); ++i) {
        std::cout << sorted[i];
        if (i < sorted.size() - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    
    // if constexpr examples
    std::cout << "\n--- if constexpr Examples ---" << std::endl;
    constexpr auto intResult = processValue(10);      // int
    constexpr auto floatResult = processValue(3.14);  // double
    constexpr auto stringResult = processValue(std::string("Hello"));
    
    std::cout << "Processed int (10): " << intResult << std::endl;
    std::cout << "Processed float (3.14): " << floatResult << std::endl;
    std::cout << "Processed string: " << stringResult << std::endl;
    
#if __cplusplus >= 202002L
    // C++20 concepts
    std::cout << "\n--- C++20 Concepts ---" << std::endl;
    constexpr auto intPower = power(2, 10);
    constexpr auto floatPower = power(2.0, 5);
    
    std::cout << "2^10 (int): " << intPower << std::endl;
    std::cout << "2.0^5 (double): " << floatPower << std::endl;
#endif
    
    return 0;
}

SFINAE and Type Traits:

sfinae_and_traits.cpp
#include <iostream>
#include <type_traits>
#include <vector>
#include <string>

// ===== SFINAE EXAMPLES =====

// Enable function only for integral types
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
safeAdd(T a, T b) {
    std::cout << "Adding integers: ";
    return a + b;
}

// Enable function only for floating point types
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
safeAdd(T a, T b) {
    std::cout << "Adding floating points: ";
    return a + b;
}

// SFINAE to detect if a type has a specific member function
template<typename T>
class has_size_method {
private:
    template<typename U>
    static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{});
    
    template<typename>
    static std::false_type test(...);
    
public:
    static constexpr bool value = decltype(test<T>(0))::value;
};

// Function that behaves differently based on whether type has size() method
template<typename T>
auto getSize(const T& container) -> typename std::enable_if<has_size_method<T>::value, size_t>::type {
    return container.size();
}

template<typename T>
auto getSize(const T&) -> typename std::enable_if<!has_size_method<T>::value, size_t>::type {
    return 1;  // Assume single element for types without size()
}

// ===== CUSTOM TYPE TRAITS =====

// Check if type is a container-like type
template<typename T>
struct is_container : std::false_type {};

template<typename T>
struct is_container<std::vector<T>> : std::true_type {};

template<>
struct is_container<std::string> : std::true_type {};

// Helper variable template (C++14)
template<typename T>
constexpr bool is_container_v = is_container<T>::value;

// ===== VARIADIC TEMPLATE METAPROGRAMMING =====

// Compile-time type checking for variadic templates
template<typename... Types>
struct all_integral;

template<>
struct all_integral<> : std::true_type {};

template<typename Head, typename... Tail>
struct all_integral<Head, Tail...> {
    static constexpr bool value = std::is_integral<Head>::value && all_integral<Tail...>::value;
};

// Variadic sum that only works with integral types
template<typename... Args>
typename std::enable_if<all_integral<Args...>::value, long long>::type
variadicSum(Args... args) {
    return (args + ...);  // C++17 fold expression
}

// ===== TAG DISPATCHING =====

struct fast_tag {};
struct safe_tag {};

// Different implementations based on tag
template<typename Iterator>
void advanceImpl(Iterator& it, size_t n, fast_tag) {
    std::cout << "Using fast advance (random access)" << std::endl;
    it += n;
}

template<typename Iterator>
void advanceImpl(Iterator& it, size_t n, safe_tag) {
    std::cout << "Using safe advance (incremental)" << std::endl;
    for (size_t i = 0; i < n; ++i) {
        ++it;
    }
}

template<typename Iterator>
void smartAdvance(Iterator& it, size_t n) {
    using category = typename std::iterator_traits<Iterator>::iterator_category;
    
    if constexpr (std::is_same_v<category, std::random_access_iterator_tag>) {
        advanceImpl(it, n, fast_tag{});
    } else {
        advanceImpl(it, n, safe_tag{});
    }
}

int main() {
    std::cout << "=== SFINAE AND TYPE TRAITS ===" << std::endl;
    
    // SFINAE function overloading
    std::cout << "\n--- SFINAE Function Overloading ---" << std::endl;
    std::cout << safeAdd(5, 3) << std::endl;
    std::cout << safeAdd(3.14, 2.86) << std::endl;
    
    // Has member detection
    std::cout << "\n--- Member Function Detection ---" << std::endl;
    std::vector<int> vec{1, 2, 3, 4, 5};
    int number = 42;
    
    std::cout << "Vector has size(): " << std::boolalpha << has_size_method<std::vector<int>>::value << std::endl;
    std::cout << "Int has size(): " << std::boolalpha << has_size_method<int>::value << std::endl;
    
    std::cout << "Size of vector: " << getSize(vec) << std::endl;
    std::cout << "Size of int: " << getSize(number) << std::endl;
    
    // Custom type traits
    std::cout << "\n--- Custom Type Traits ---" << std::endl;
    std::cout << "vector<int> is container: " << std::boolalpha << is_container_v<std::vector<int>> << std::endl;
    std::cout << "string is container: " << std::boolalpha << is_container_v<std::string> << std::endl;
    std::cout << "int is container: " << std::boolalpha << is_container_v<int> << std::endl;
    
    // Variadic template metaprogramming
    std::cout << "\n--- Variadic Template Constraints ---" << std::endl;
    std::cout << "All integral check (int, long, char): " << std::boolalpha 
              << all_integral<int, long, char>::value << std::endl;
    std::cout << "All integral check (int, double, char): " << std::boolalpha 
              << all_integral<int, double, char>::value << std::endl;
    
    std::cout << "Sum of integers: " << variadicSum(1, 2, 3, 4, 5) << std::endl;
    // variadicSum(1, 2.5, 3);  // Would cause compilation error
    
    // Tag dispatching
    std::cout << "\n--- Tag Dispatching ---" << std::endl;
    std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto vecIt = numbers.begin();
    smartAdvance(vecIt, 3);
    std::cout << "Advanced to: " << *vecIt << std::endl;
    
    return 0;
}

Metaprogramming Techniques Comparison:

Technique C++ Version Syntax Complexity Compile Time Best Use Case
Template Recursion C++98 High Slow Complex type computations
SFINAE C++98 Very High Medium Conditional template instantiation
constexpr Functions C++11/14 Low Fast Compile-time calculations
if constexpr C++17 Very Low Fast Conditional compilation
Concepts C++20 Low Fast Template constraints

Best Practices for Template Metaprogramming:

Modern Approaches

  • Prefer constexpr functions over template recursion
  • Use if constexpr instead of SFINAE when possible
  • Leverage std::type_traits for type checking
  • Use concepts (C++20) for cleaner template constraints
  • Prefer variable templates over struct-based traits

Avoid

  • Deep template recursion (causes slow compilation)
  • Complex SFINAE when simpler alternatives exist
  • Metaprogramming for simple calculations
  • Unclear template parameter names
  • Overusing template metaprogramming

Evolution of Metaprogramming: C++ metaprogramming has evolved from complex template tricks to elegant, readable code. Modern C++ (C++11 and later) provides much cleaner alternatives to classical template metaprogramming techniques.

Performance Benefits: Template metaprogramming moves computations from runtime to compile-time, resulting in zero runtime overhead. Your programs run faster because the work is already done when the code is compiled!

Compilation Impact: While metaprogramming improves runtime performance, it can significantly increase compilation time. Use profiling tools to measure compilation impact and optimize accordingly.

Advanced Design Patterns

Design patterns are proven solutions to common programming problems. In modern C++, many patterns can be implemented more elegantly using templates, smart pointers, and lambda expressions.

Modern C++ Pattern Categories:

Creational

Object creation mechanisms

Structural

Object composition and relationships

Behavioral

Communication between objects

Modern C++

C++11/14/17/20 specific patterns

modern_design_patterns.cpp
#include <iostream>
#include <memory>
#include <vector>
#include <functional>
#include <unordered_map>
#include <string>
#include <any>
#include <variant>

// ===== SINGLETON PATTERN (Modern C++11) =====
class ModernSingleton {
private:
    ModernSingleton() = default;
    
public:
    // Thread-safe singleton using static local variable
    static ModernSingleton& getInstance() {
        static ModernSingleton instance;
        return instance;
    }
    
    // Delete copy constructor and assignment operator
    ModernSingleton(const ModernSingleton&) = delete;
    ModernSingleton& operator=(const ModernSingleton&) = delete;
    
    void doSomething() {
        std::cout << "Singleton instance doing work" << std::endl;
    }
};

// ===== FACTORY PATTERN (Using Smart Pointers) =====
class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const = 0;
    virtual std::string getType() const = 0;
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a Circle" << std::endl;
    }
    std::string getType() const override { return "Circle"; }
};

class Rectangle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a Rectangle" << std::endl;
    }
    std::string getType() const override { return "Rectangle"; }
};

class ShapeFactory {
public:
    static std::unique_ptr<Shape> createShape(const std::string& type) {
        if (type == "circle") {
            return std::make_unique<Circle>();
        } else if (type == "rectangle") {
            return std::make_unique<Rectangle>();
        }
        return nullptr;
    }
};

// ===== OBSERVER PATTERN (Using Function Objects) =====
template<typename... Args>
class Signal {
private:
    std::vector<std::function<void(Args...)>> slots;
    
public:
    // Connect a slot (function/lambda) to the signal
    void connect(std::function<void(Args...)> slot) {
        slots.push_back(std::move(slot));
    }
    
    // Emit signal to all connected slots
    void emit(Args... args) {
        for (auto& slot : slots) {
            slot(args...);
        }
    }
};

class Publisher {
private:
    Signal<const std::string&>> messageSignal;
    
public:
    void connectObserver(std::function<void(const std::string&)> observer) {
        messageSignal.connect(std::move(observer));
    }
    
    void publishMessage(const std::string& message) {
        std::cout << "Publishing: " << message << std::endl;
        messageSignal.emit(message);
    }
};

// ===== STRATEGY PATTERN (Using Lambdas) =====
class Calculator {
private:
    std::function<double(double, double)> strategy;
    
public:
    void setStrategy(std::function<double(double, double)> newStrategy) {
        strategy = std::move(newStrategy);
    }
    
    double execute(double a, double b) {
        return strategy ? strategy(a, b) : 0.0;
    }
};

// ===== VISITOR PATTERN (Using std::variant) =====
struct AddOperation { double value; };
struct MultiplyOperation { double value; };
struct PrintOperation {};

using Operation = std::variant<AddOperation, MultiplyOperation, PrintOperation>;

class ModernCalculatorWithVisitor {
private:
    double value = 0.0;
    
public:
    void process(const Operation& op) {
        std::visit([this](const auto& operation) {
            using T = std::decay_t<decltype(operation)>;
            if constexpr (std::is_same_v<T, AddOperation>) {
                value += operation.value;
            } else if constexpr (std::is_same_v<T, MultiplyOperation>) {
                value *= operation.value;
            } else if constexpr (std::is_same_v<T, PrintOperation>) {
                std::cout << "Current value: " << value << std::endl;
            }
        }, op);
    }
    
    double getValue() const { return value; }
};

// ===== COMMAND PATTERN (Using Function Objects) =====
class CommandProcessor {
private:
    std::vector<std::function<void()>> commands;
    std::vector<std::function<void()>> undoCommands;
    
public:
    void execute(std::function<void()> command, std::function<void()> undoCommand) {
        command();
        commands.push_back(std::move(command));
        undoCommands.push_back(std::move(undoCommand));
    }
    
    void undo() {
        if (!undoCommands.empty()) {
            undoCommands.back()();
            undoCommands.pop_back();
            commands.pop_back();
        }
    }
};

// ===== PIMPL PATTERN (Pointer to Implementation) =====
class Widget {
private:
    class Impl;
    std::unique_ptr<Impl> pImpl;
    
public:
    Widget();
    ~Widget();
    Widget(const Widget& other);
    Widget& operator=(const Widget& other);
    Widget(Widget&& other) noexcept;
    Widget& operator=(Widget&& other) noexcept;
    
    void doSomething();
    int getValue() const;
};

// Implementation would be in .cpp file
class Widget::Impl {
public:
    int value = 42;
    std::string data = "Hidden implementation";
    
    void doWork() {
        std::cout << "Widget implementation working with: " << data << std::endl;
    }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
Widget::Widget(const Widget& other) : pImpl(std::make_unique<Impl>(*other.pImpl)) {}
Widget& Widget::operator=(const Widget& other) {
    if (this != &other) {
        *pImpl = *other.pImpl;
    }
    return *this;
}
Widget::Widget(Widget&& other) noexcept = default;
Widget& Widget::operator=(Widget&& other) noexcept = default;

void Widget::doSomething() { pImpl->doWork(); }
int Widget::getValue() const { return pImpl->value; }

// ===== CRTP PATTERN (Curiously Recurring Template Pattern) =====
template<typename Derived>
class Printable {
public:
    void print() const {
        static_cast<const Derived*>(this)->printImpl();
    }
};

class Document : public Printable<Document> {
private:
    std::string content;
    
public:
    Document(const std::string& text) : content(text) {}
    
    void printImpl() const {
        std::cout << "Document: " << content << std::endl;
    }
};

class Image : public Printable<Image> {
private:
    std::string filename;
    
public:
    Image(const std::string& file) : filename(file) {}
    
    void printImpl() const {
        std::cout << "Image: " << filename << std::endl;
    }
};

int main() {
    std::cout << "=== MODERN C++ DESIGN PATTERNS ===" << std::endl;
    
    // Singleton Pattern
    std::cout << "\n--- Singleton Pattern ---" << std::endl;
    auto& singleton = ModernSingleton::getInstance();
    singleton.doSomething();
    
    // Factory Pattern
    std::cout << "\n--- Factory Pattern ---" << std::endl;
    auto circle = ShapeFactory::createShape("circle");
    auto rectangle = ShapeFactory::createShape("rectangle");
    if (circle) circle->draw();
    if (rectangle) rectangle->draw();
    
    // Observer Pattern
    std::cout << "\n--- Observer Pattern ---" << std::endl;
    Publisher publisher;
    
    // Lambda observers
    publisher.connectObserver([](const std::string& msg) {
        std::cout << "Observer 1 received: " << msg << std::endl;
    });
    
    publisher.connectObserver([](const std::string& msg) {
        std::cout << "Observer 2 logged: " << msg << std::endl;
    });
    
    publisher.publishMessage("Hello Observers!");
    
    // Strategy Pattern
    std::cout << "\n--- Strategy Pattern ---" << std::endl;
    Calculator calc;
    
    // Addition strategy
    calc.setStrategy([](double a, double b) { return a + b; });
    std::cout << "5 + 3 = " << calc.execute(5, 3) << std::endl;
    
    // Multiplication strategy
    calc.setStrategy([](double a, double b) { return a * b; });
    std::cout << "5 * 3 = " << calc.execute(5, 3) << std::endl;
    
    // Visitor Pattern with std::variant
    std::cout << "\n--- Visitor Pattern (std::variant) ---" << std::endl;
    ModernCalculatorWithVisitor calculator;
    
    calculator.process(AddOperation{10.0});
    calculator.process(MultiplyOperation{2.0});
    calculator.process(PrintOperation{});
    calculator.process(AddOperation{5.0});
    calculator.process(PrintOperation{});
    
    // Command Pattern
    std::cout << "\n--- Command Pattern ---" << std::endl;
    CommandProcessor processor;
    int value = 0;
    
    // Execute command with undo capability
    processor.execute(
        [&value]() { value += 10; std::cout << "Added 10, value: " << value << std::endl; },
        [&value]() { value -= 10; std::cout << "Undid add 10, value: " << value << std::endl; }
    );
    
    processor.execute(
        [&value]() { value *= 2; std::cout << "Multiplied by 2, value: " << value << std::endl; },
        [&value]() { value /= 2; std::cout << "Undid multiply by 2, value: " << value << std::endl; }
    );
    
    processor.undo();  // Undo multiply
    processor.undo();  // Undo add
    
    // PIMPL Pattern
    std::cout << "\n--- PIMPL Pattern ---" << std::endl;
    Widget widget;
    widget.doSomething();
    std::cout << "Widget value: " << widget.getValue() << std::endl;
    
    // CRTP Pattern
    std::cout << "\n--- CRTP Pattern ---" << std::endl;
    Document doc("Hello World");
    Image img("photo.jpg");
    
    doc.print();
    img.print();
    
    return 0;
}

Pattern Selection Guide:

Need single instance?
Singleton
Object creation abstraction?
Factory
Event notifications?
Observer/Signal-Slot
Runtime algorithm selection?
Strategy
Hide implementation details?
PIMPL
Compile-time polymorphism?
CRTP

Modern C++ Advantage: Many traditional design patterns are simplified or replaced by modern C++ features like lambdas, std::function, smart pointers, and templates, resulting in cleaner and more efficient code.

Performance Optimization

Performance optimization in C++ involves understanding the cost of operations, memory access patterns, compiler optimizations, and modern CPU architectures. Learn to write fast code without premature optimization.

Performance Optimization Principles:

📏 Measure First

Always profile before optimizing

🎯 Optimize Hotspots

Focus on code that runs most frequently

🧠 Cache-Friendly

Design for modern CPU cache hierarchies

⚡ Compile-Time

Move computations from runtime to compile-time

performance_optimization.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
#include <numeric>
#include <random>
#include <memory>
#include <string>

class PerformanceTester {
public:
    template<typename Func>
    static double measureTime(Func&& func, const std::string& name) {
        auto start = std::chrono::high_resolution_clock::now();
        func();
        auto end = std::chrono::high_resolution_clock::now();
        
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        double ms = duration.count() / 1000.0;
        std::cout << name << ": " << ms << " ms" << std::endl;
        return ms;
    }
};

// ===== MEMORY ACCESS PATTERNS =====
class MemoryAccessDemo {
public:
    static void cacheLocalityDemo() {
        const size_t size = 10000000;
        std::vector<int> data(size);
        std::iota(data.begin(), data.end(), 0);
        
        std::cout << "\n=== Cache Locality Demo ===" << std::endl;
        
        // Sequential access (cache-friendly)
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < size; ++i) {
                sum += data[i];
            }
            volatile auto result = sum; // Prevent optimization
        }, "Sequential access");
        
        // Random access (cache-unfriendly)
        std::vector<size_t> indices(size);
        std::iota(indices.begin(), indices.end(), 0);
        std::random_device rd;
        std::mt19937 gen(rd());
        std::shuffle(indices.begin(), indices.end(), gen);
        
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < size / 100; ++i) { // Smaller subset for fairness
                sum += data[indices[i]];
            }
            volatile auto result = sum;
        }, "Random access (1/100 subset)");
    }
    
    static void memoryLayoutDemo() {
        std::cout << "\n=== Memory Layout Demo ===" << std::endl;
        
        const size_t count = 1000000;
        
        // Array of Structures (AoS) - poor cache usage
        struct PointAoS {
            double x, y, z;
            int id;
            char padding[100]; // Simulate larger struct
        };
        
        std::vector<PointAoS> aos_points(count);
        
        // Structure of Arrays (SoA) - better cache usage
        struct PointsSoA {
            std::vector<double> x, y, z;
            std::vector<int> id;
            
            PointsSoA(size_t size) : x(size), y(size), z(size), id(size) {}
        };
        
        PointsSoA soa_points(count);
        
        // Initialize data
        for (size_t i = 0; i < count; ++i) {
            aos_points[i] = {double(i), double(i*2), double(i*3), int(i)};
            soa_points.x[i] = double(i);
            soa_points.y[i] = double(i*2);
            soa_points.z[i] = double(i*3);
            soa_points.id[i] = int(i);
        }
        
        // Test: sum only x coordinates
        PerformanceTester::measureTime([&]() {
            double sum = 0;
            for (const auto& point : aos_points) {
                sum += point.x;
            }
            volatile auto result = sum;
        }, "AoS sum x coordinates");
        
        PerformanceTester::measureTime([&]() {
            double sum = 0;
            for (double x : soa_points.x) {
                sum += x;
            }
            volatile auto result = sum;
        }, "SoA sum x coordinates");
    }
};

// ===== ALGORITHMIC OPTIMIZATIONS =====
class AlgorithmicOptimizations {
public:
    static void containerChoiceDemo() {
        std::cout << "\n=== Container Choice Demo ===" << std::endl;
        
        const size_t operations = 100000;
        
        // Vector vs List for different operations
        std::vector<int> vec;
        std::list<int> lst;
        
        // Random access (vector wins)
        vec.resize(operations);
        std::iota(vec.begin(), vec.end(), 0);
        
        for (size_t i = 0; i < operations; ++i) {
            lst.push_back(i);
        }
        
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < operations / 100; ++i) {
                sum += vec[i * 100];
            }
            volatile auto result = sum;
        }, "Vector random access");
        
        // Insertion at beginning (list wins)
        std::vector<int> vec2;
        std::list<int> lst2;
        
        PerformanceTester::measureTime([&]() {
            for (size_t i = 0; i < operations / 1000; ++i) {
                vec2.insert(vec2.begin(), i);
            }
        }, "Vector insert at beginning");
        
        PerformanceTester::measureTime([&]() {
            for (size_t i = 0; i < operations / 1000; ++i) {
                lst2.push_front(i);
            }
        }, "List insert at beginning");
    }
    
    static void algorithmChoiceDemo() {
        std::cout << "\n=== Algorithm Choice Demo ===" << std::endl;
        
        const size_t size = 1000000;
        std::vector<int> data(size);
        std::iota(data.begin(), data.end(), 0);
        
        std::random_device rd;
        std::mt19937 gen(rd());
        std::shuffle(data.begin(), data.end(), gen);
        
        auto data_copy1 = data;
        auto data_copy2 = data;
        
        // Sorting comparison
        PerformanceTester::measureTime([&]() {
            std::sort(data_copy1.begin(), data_copy1.end());
        }, "std::sort");
        
        PerformanceTester::measureTime([&]() {
            std::stable_sort(data_copy2.begin(), data_copy2.end());
        }, "std::stable_sort");
        
        // Search comparison (on sorted data)
        std::sort(data.begin(), data.end());
        int target = data[size / 2];
        
        PerformanceTester::measureTime([&]() {
            for (int i = 0; i < 1000; ++i) {
                auto it = std::find(data.begin(), data.end(), target);
                volatile auto result = it;
            }
        }, "Linear search (1000x)");
        
        PerformanceTester::measureTime([&]() {
            for (int i = 0; i < 1000; ++i) {
                auto it = std::lower_bound(data.begin(), data.end(), target);
                volatile auto result = it;
            }
        }, "Binary search (1000x)");
    }
};

// ===== COMPILE-TIME OPTIMIZATIONS =====
class CompileTimeOptimizations {
public:
    // Compile-time factorial
    static constexpr long long factorial(int n) {
        return (n <= 1) ? 1 : n * factorial(n - 1);
    }
    
    // Template metaprogramming for compile-time computation
    template<int N>
    struct PowerOfTwo {
        static constexpr long long value = 2 * PowerOfTwo<N-1>::value;
    };
    
    template<>
    struct PowerOfTwo<0> {
        static constexpr long long value = 1;
    };
    
    static void constexprDemo() {
        std::cout << "\n=== Compile-time vs Runtime Demo ===" << std::endl;
        
        // Runtime computation
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (int i = 0; i < 1000000; ++i) {
                long long fact = 1;
                for (int j = 1; j <= 10; ++j) {
                    fact *= j;
                }
                sum += fact;
            }
            volatile auto result = sum;
        }, "Runtime factorial calculation");
        
        // Compile-time computation
        PerformanceTester::measureTime([&]() {
            constexpr long long fact = factorial(10);
            long long sum = 0;
            for (int i = 0; i < 1000000; ++i) {
                sum += fact;
            }
            volatile auto result = sum;
        }, "Compile-time factorial (constexpr)");
        
        std::cout << "Power of 2^10: " << PowerOfTwo<10>::value << " (computed at compile-time)" << std::endl;
    }
};

// ===== MODERN C++ OPTIMIZATIONS =====
class ModernCppOptimizations {
public:
    static void moveSemanticsBenefit() {
        std::cout << "\n=== Move Semantics Benefit ===" << std::endl;
        
        const size_t size = 1000000;
        std::vector<std::string> strings;
        
        // Prepare large strings
        for (size_t i = 0; i < 1000; ++i) {
            strings.emplace_back(size, 'A' + (i % 26));
        }
        
        // Copy semantics
        PerformanceTester::measureTime([&]() {
            std::vector<std::string> copied;
            for (const auto& str : strings) {
                copied.push_back(str); // Copy
            }
            volatile auto result = copied.size();
        }, "Copy semantics");
        
        // Move semantics (simulated)
        PerformanceTester::measureTime([&]() {
            std::vector<std::string> moved;
            auto strings_copy = strings; // Work with copy
            for (auto& str : strings_copy) {
                moved.push_back(std::move(str)); // Move
            }
            volatile auto result = moved.size();
        }, "Move semantics");
    }
    
    static void smartPointerOverhead() {
        std::cout << "\n=== Smart Pointer Overhead ===" << std::endl;
        
        const size_t operations = 10000000;
        
        // Raw pointer
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < operations; ++i) {
                int* ptr = new int(i);
                sum += *ptr;
                delete ptr;
            }
            volatile auto result = sum;
        }, "Raw pointer new/delete");
        
        // unique_ptr
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < operations; ++i) {
                auto ptr = std::make_unique<int>(i);
                sum += *ptr;
            }
            volatile auto result = sum;
        }, "unique_ptr");
        
        // shared_ptr
        PerformanceTester::measureTime([&]() {
            long long sum = 0;
            for (size_t i = 0; i < operations; ++i) {
                auto ptr = std::make_shared<int>(i);
                sum += *ptr;
            }
            volatile auto result = sum;
        }, "shared_ptr");
    }
};

int main() {
    std::cout << "=== C++ PERFORMANCE OPTIMIZATION DEMONSTRATION ===" << std::endl;
    
    // Memory access patterns
    MemoryAccessDemo::cacheLocalityDemo();
    MemoryAccessDemo::memoryLayoutDemo();
    
    // Algorithmic choices
    AlgorithmicOptimizations::containerChoiceDemo();
    AlgorithmicOptimizations::algorithmChoiceDemo();
    
    // Compile-time optimizations
    CompileTimeOptimizations::constexprDemo();
    
    // Modern C++ features
    ModernCppOptimizations::moveSemanticsBenefit();
    ModernCppOptimizations::smartPointerOverhead();
    
    std::cout << "\n=== Performance testing complete ===" << std::endl;
    std::cout << "Note: Results may vary based on compiler, optimization flags, and hardware" << std::endl;
    
    return 0;
}

Performance Optimization Checklist:

Memory

  • Use stack allocation when possible
  • Prefer std::array over C arrays
  • Consider object pooling for frequent allocations
  • Align data structures for cache efficiency
  • Use Structure of Arrays for better cache locality

Algorithms

  • Choose the right container for your use case
  • Use STL algorithms instead of hand-written loops
  • Consider parallel algorithms (C++17)
  • Profile to find algorithmic bottlenecks
  • Use Big O notation to guide choices

Compiler

  • Use constexpr for compile-time computation
  • Enable compiler optimizations (-O2, -O3)
  • Use LTO (Link Time Optimization)
  • Profile-guided optimization (PGO)
  • Understand compiler auto-vectorization

Modern C++

  • Use move semantics to avoid copies
  • Prefer in-place construction (emplace)
  • Use string_view to avoid string copies
  • Consider std::optional vs exceptions
  • Use structured bindings for readability

Premature Optimization Warning: "Premature optimization is the root of all evil" - Donald Knuth. Always profile first, optimize second. Focus on correctness and readability before micro-optimizations.

Profiling Tools: Use tools like Valgrind, Intel VTune, Google Benchmark, or built-in compiler profilers to measure actual performance before and after optimizations.

Advanced Best Practices

Master the advanced techniques and patterns that separate expert C++ developers from beginners. These practices ensure your code is efficient, maintainable, and follows modern C++ idioms.

Performance

  • Use move semantics to avoid unnecessary copies
  • Prefer stack allocation over heap when possible
  • Use constexpr for compile-time computations
  • Profile before optimizing
  • Prefer algorithms over hand-written loops

Safety

  • Always use RAII for resource management
  • Prefer smart pointers over raw pointers
  • Use const correctness everywhere
  • Handle exceptions properly
  • Avoid undefined behavior

Code Quality

  • Follow the Rule of Zero/Three/Five
  • Use meaningful names for everything
  • Keep functions small and focused
  • Prefer composition over inheritance
  • Write self-documenting code

Modern C++

  • Use auto for type deduction
  • Prefer range-based for loops
  • Use lambda expressions appropriately
  • Leverage std::optional for nullable values
  • Use structured bindings (C++17)

Congratulations! You've completed the Advanced C++ tutorial! You now have the knowledge to write high-performance, modern C++ code. Continue practicing with real projects and exploring the latest C++ standards to keep improving your skills.