# An Overview of Templates

## Templates:

\
Templates in C++ allow you to define generic types and functions that can work with different data types without explicitly specifying the type. Templates are defined using the `template` keyword. Here's an example of a template function that swaps two values:

```cpp
template <typename T>
void swapValues(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    swapValues(x, y);  // Swaps the values of x and y
    cout << "x: " << x << ", y: " << y << endl;
    
    double a = 3.14, b = 2.71;
    swapValues(a, b);  // Swaps the values of a and b
    cout << "a: " << a << ", b: " << b << endl;
    
    return 0;
}
```

## Overloading Functions:

\
Function overloading allows you to define multiple functions with the same name but different parameters. The compiler determines the appropriate function to call based on the arguments provided. Here's an example of function overloading:

```cpp
void printNumber(int num) {
    cout << "Integer number: " << num << endl;
}

void printNumber(double num) {
    cout << "Floating-point number: " << num << endl;
}

int main() {
    int x = 5;
    double y = 3.14;
    
    printNumber(x);  // Calls the printNumber function with int parameter
    printNumber(y);  // Calls the printNumber function with double parameter
    
    return 0;
}
```

## Template Functions:

\
Template functions are functions that can work with multiple types using a template parameter. Here's an example of a template function that finds the maximum of two values:

```cpp
template <typename T>
T findMax(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 5, y = 10;
    cout << "Max: " << findMax(x, y) << endl;  // Finds the maximum of two integers
    
    double a = 3.14, b = 2.71;
    cout << "Max: " << findMax(a, b) << endl;  // Finds the maximum of two doubles
    
    return 0;
}
```

## Specializing a Template Function:

\
Template function specialization allows you to provide a specific implementation for a particular type. Here's an example of template function specialization for finding the maximum of two strings:

```cpp
template <>
string findMax(string a, string b) {
    return (a.length() > b.length()) ? a : b;
}

int main() {
    string str1 = "Hello";
    string str2 = "World";
    
    cout << "Max: " << findMax(str1, str2) << endl;  // Finds the longer string
    
    return 0;
}
```

## Disambiguation under Specialization:

\
When specializing template functions, it's important to disambiguate them from the generic template function. Here's an example showing how to disambiguate a specialized template function:

```cpp
template <typename T>
void process(T value) {
    cout << "Generic Template" << endl;
}

template <>
void process<int>(int value) {
    cout << "Specialized Template for int" << endl;
}

int main() {
    int x = 5;
    process(x);  // Calls the specialized template for int
    
    double y = 3.14;
    process(y);  // Calls the generic template
    
    return 0;
}
```

## Template Classes:

\
Templates can also be used to create generic classes. Here's an example of a template class called `Stack` that implements a stack data structure:

```cpp
template <typename T>
class Stack {
private:
    vector<T> elements;

public:
    void push(T value) {
        elements.push_back(value);
    }

    T pop() {
        T value = elements.back();
        elements.pop_back();
        return value;
    }
};

int main() {
    Stack<int> intStack;
    intStack.push(5);
    intStack.push(10);
    intStack.push(15);
    cout << intStack.pop() << endl;  // Outputs 15

    Stack<string> stringStack;
    stringStack.push("Hello");
    stringStack.push("World");
    cout << stringStack.pop() << endl;  // Outputs "World"

    return 0;
}
```

## An Array Template Class:

\
Here's an example of a template class called `Array` that represents a dynamic array:

```cpp
template <typename T>
class Array {
private:
    T* elements;
    int size;

public:
    Array(int size) : size(size) {
        elements = new T[size];
    }

    ~Array() {
        delete[] elements;
    }

    T& operator[](int index) {
        return elements[index];
    }
};

int main() {
    Array<int> intArray(5);
    intArray[0] = 10;
    intArray[1] = 20;
    cout << intArray[0] << endl;  // Outputs 10

    Array<double> doubleArray(3);
    doubleArray[0] = 3.14;
    cout << doubleArray[0] << endl;  // Outputs 3.14

    return 0;
}
```

## Instantiating a Template Class Object:

\
To create an object of a template class, you need to specify the template parameter. Here's an example of instantiating a template class object:

```cpp
template <typename T>
class MyClass {
private:
    T value;

public:
    MyClass(T value) : value(value) {}

    void print() {
        cout << "Value: " << value << endl;
    }
};

int main() {
    MyClass<int> obj1(5);
    obj1.print();  // Outputs "Value: 5"

    MyClass<string> obj2("Hello");
    obj2.print();  // Outputs "Value: Hello"

    return 0;
}
```

## Friends of Template Classes:

\
A template class can declare other classes or functions as friends using the `friend` keyword. Here's an example of a template class with a friend function:

```cpp
template <typename T>
class MyTemplateClass {
private:
    T value;

public:
    MyTemplateClass(T value) : value(value) {}

    template <typename U>
    friend void printValue(const MyTemplateClass<U>& obj);
};

template <typename U>
void printValue(const MyTemplateClass<U>& obj) {
    cout << "Value: " << obj.value << endl;
}

int main() {
    MyTemplateClass<int> obj(5);
    printValue(obj);  // Outputs "Value: 5"

    return 0;
}
```

## Templates with Multiple Type Parameters:

\
Templates can have multiple type parameters, allowing you to work with multiple types simultaneously. Here's an example of a template function with multiple type parameters:

```cpp
template <typename T, typename U>
void printPair(T first, U second) {
    cout << "Pair: " << first << ", " << second << endl;
}

int main() {
    int x = 5;
    double y = 3.14;
    printPair(x, y);  // Outputs "Pair: 5, 3.14"

    string str = "Hello";
    char ch = '!';
    printPair(str, ch);  // Outputs "Pair: Hello, !"

    return 0;
}
```

## Non-Class-type Parameters for Template Classes:

\
Template classes can also have non-class-type parameters, such as integers or enums. Here's an example of a template class with a non-class-type parameter:

```cpp
template <int Size>
class FixedArray {
private:
    int array[Size];

public:
    void setValue(int index, int value) {
        array[index] = value;
    }

    int getValue(int index) {
        return array[index];
    }
};

int main() {
    FixedArray<5> arr;
    arr.setValue(0, 10);
    cout << arr.getValue(0) << endl;  // Outputs 10

    FixedArray<10> arr2;
    arr2.setValue(5, 20);
    cout << arr2.getValue(5) << endl;  // Outputs 20

    return 0;
}
```

## Comments Regarding Templates:

When working with templates, it's important to keep in mind a few considerations:

* Templates are resolved at compile-time, so template code must be visible during compilation.
* Template functions can be defined in header files to make them accessible across multiple source files.
* Templates can have default template arguments, allowing you to provide a default type if one is not explicitly specified.
* Templates can be specialized for specific types to provide custom behavior for those types.

## Templates and Inheritance:

Templates and inheritance can be used together in C++ to create generic base classes and derived classes. Templates allow you to define a class or function that can work with multiple types, while inheritance allows you to create a hierarchy of classes with shared functionality.

Here's an example that demonstrates how templates and inheritance can be combined:

```cpp
template <typename T>
class BaseClass {
protected:
    T value;

public:
    BaseClass(T value) : value(value) {}

    void printValue() {
        cout << "Value: " << value << endl;
    }
};

template <typename T>
class DerivedClass : public BaseClass<T> {
public:
    DerivedClass(T value) : BaseClass<T>(value) {}

    void printDoubleValue() {
        cout << "Double Value: " << this->value * 2 << endl;
    }
};

int main() {
    DerivedClass<int> obj(5);
    obj.printValue();         // Outputs "Value: 5"
    obj.printDoubleValue();   // Outputs "Double Value: 10"

    DerivedClass<double> obj2(3.14);
    obj2.printValue();        // Outputs "Value: 3.14"
    obj2.printDoubleValue();  // Outputs "Double Value: 6.28"

    return 0;
}
```

In this example, we have a template base class `BaseClass`, which has a member variable `value` of type `T` and a member function `printValue()` that prints the value. The derived class `DerivedClass` is also a template class that inherits from `BaseClass<T>`. It adds a new member function `printDoubleValue()`, which doubles the value and prints it.

By using templates, we can create instances of `BaseClass` and `DerivedClass` with different types. In the `main()` function, we create objects of `DerivedClass` with `int` and `double` types and call their member functions to demonstrate the inheritance and template functionality.

Templates and inheritance together provide a powerful mechanism for creating generic and reusable code that can work with different types.
