cpp
Templates
07_Templates⚙️cpp
/**
* ============================================================
* C++ FUNCTION TEMPLATES
* ============================================================
*
* This file covers:
* - Function template basics
* - Type deduction
* - Multiple template parameters
* - Non-type template parameters
* - Template specialization
* - Overloading templates
*
* Compile: g++ -std=c++17 -Wall 01_function_templates.cpp -o func_templates
* Run: ./func_templates
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <algorithm>
#include <cstring>
using namespace std;
// ============================================================
// PART 1: BASIC FUNCTION TEMPLATE
// ============================================================
// Generic function - works with any type
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Alternative syntax: template <class T> is equivalent
template <class T>
T minimum(T a, T b) {
return (a < b) ? a : b;
}
// Template with reference parameters
template <typename T>
void swap_values(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// ============================================================
// PART 2: MULTIPLE TEMPLATE PARAMETERS
// ============================================================
// Two different type parameters
template <typename T1, typename T2>
void printPair(const T1& first, const T2& second) {
cout << "(" << first << ", " << second << ")" << endl;
}
// Return type as template parameter
template <typename R, typename T1, typename T2>
R add(T1 a, T2 b) {
return static_cast<R>(a) + static_cast<R>(b);
}
// Auto return type deduction (C++14+)
template <typename T1, typename T2>
auto multiply(T1 a, T2 b) {
return a * b;
}
// ============================================================
// PART 3: NON-TYPE TEMPLATE PARAMETERS
// ============================================================
// Array with size as template parameter
template <typename T, int SIZE>
class FixedArray {
private:
T data[SIZE];
public:
T& operator[](int index) {
return data[index];
}
const T& operator[](int index) const {
return data[index];
}
int size() const { return SIZE; }
void fill(const T& value) {
for (int i = 0; i < SIZE; i++) {
data[i] = value;
}
}
void print() const {
cout << "[";
for (int i = 0; i < SIZE; i++) {
cout << data[i];
if (i < SIZE - 1) cout << ", ";
}
cout << "]" << endl;
}
};
// Function with non-type parameter
template <typename T, int N>
T arraySum(const T (&arr)[N]) {
T sum = T(); // Value initialization
for (int i = 0; i < N; i++) {
sum += arr[i];
}
return sum;
}
// ============================================================
// PART 4: TEMPLATE SPECIALIZATION
// ============================================================
// Primary template
template <typename T>
void printType(const T& value) {
cout << "Generic type: " << value << endl;
}
// Full specialization for const char*
template <>
void printType<const char*>(const char* const& value) {
cout << "C-string: \"" << value << "\" (length: " << strlen(value) << ")" << endl;
}
// Full specialization for bool
template <>
void printType<bool>(const bool& value) {
cout << "Boolean: " << (value ? "TRUE" : "FALSE") << endl;
}
// Primary template for compare
template <typename T>
int compare(const T& a, const T& b) {
if (a < b) return -1;
if (b < a) return 1;
return 0;
}
// Specialization for C-strings
template <>
int compare<const char*>(const char* const& a, const char* const& b) {
return strcmp(a, b);
}
// ============================================================
// PART 5: OVERLOADING TEMPLATES
// ============================================================
// Template version
template <typename T>
void process(T value) {
cout << "Template process: " << value << endl;
}
// Non-template overload (preferred for exact match)
void process(int value) {
cout << "Int overload process: " << value << endl;
}
// Overload for pointers
template <typename T>
void process(T* ptr) {
cout << "Pointer process: " << *ptr << endl;
}
// ============================================================
// PART 6: TEMPLATE WITH CONCEPTS (C++20 preview)
// ============================================================
// Without concepts - SFINAE (complex)
template <typename T>
typename enable_if<is_arithmetic<T>::value, T>::type
numericOnly(T value) {
return value * 2;
}
// C++20 concepts (cleaner syntax)
// template <typename T>
// requires std::integral<T>
// T integerOnly(T value) {
// return value * 2;
// }
// ============================================================
// PART 7: VARIADIC TEMPLATES
// ============================================================
// Base case for recursion
void printAll() {
cout << endl;
}
// Variadic template
template <typename T, typename... Args>
void printAll(const T& first, const Args&... rest) {
cout << first;
if (sizeof...(rest) > 0) cout << ", ";
printAll(rest...); // Recursive call
}
// Fold expression (C++17)
template <typename... Args>
auto sumAll(Args... args) {
return (args + ...); // Fold expression
}
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ FUNCTION TEMPLATES" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Basic Function Templates
// ========================================================
cout << "--- DEMO 1: BASIC TEMPLATES ---" << endl << endl;
cout << "maximum(5, 3) = " << maximum(5, 3) << endl;
cout << "maximum(3.14, 2.72) = " << maximum(3.14, 2.72) << endl;
cout << "maximum('a', 'z') = " << maximum('a', 'z') << endl;
cout << "maximum(string(\"apple\"), string(\"banana\")) = "
<< maximum(string("apple"), string("banana")) << endl;
cout << "\nminimum(10, 20) = " << minimum(10, 20) << endl;
int x = 5, y = 10;
cout << "\nBefore swap: x=" << x << ", y=" << y << endl;
swap_values(x, y);
cout << "After swap: x=" << x << ", y=" << y << endl;
// Explicit type specification
cout << "\nExplicit: maximum<double>(5, 3.5) = " << maximum<double>(5, 3.5) << endl;
cout << endl;
// ========================================================
// DEMO 2: Multiple Type Parameters
// ========================================================
cout << "--- DEMO 2: MULTIPLE TYPE PARAMETERS ---" << endl << endl;
printPair(1, "hello");
printPair(3.14, 42);
printPair("name", string("value"));
// Return type specified
cout << "\nadd<double>(5, 3.5) = " << add<double>(5, 3.5) << endl;
cout << "add<int>(5, 3.5) = " << add<int>(5, 3.5) << endl;
// Auto return type
cout << "\nmultiply(5, 3.5) = " << multiply(5, 3.5) << endl;
cout << "multiply(3, 4) = " << multiply(3, 4) << endl;
cout << endl;
// ========================================================
// DEMO 3: Non-Type Parameters
// ========================================================
cout << "--- DEMO 3: NON-TYPE PARAMETERS ---" << endl << endl;
FixedArray<int, 5> arr;
for (int i = 0; i < arr.size(); i++) {
arr[i] = i * 10;
}
cout << "FixedArray<int, 5>: ";
arr.print();
FixedArray<double, 3> darr;
darr.fill(3.14);
cout << "FixedArray<double, 3>: ";
darr.print();
int nums[] = {1, 2, 3, 4, 5};
cout << "\narraySum({1,2,3,4,5}) = " << arraySum(nums) << endl;
double dnums[] = {1.1, 2.2, 3.3};
cout << "arraySum({1.1,2.2,3.3}) = " << arraySum(dnums) << endl;
cout << endl;
// ========================================================
// DEMO 4: Template Specialization
// ========================================================
cout << "--- DEMO 4: TEMPLATE SPECIALIZATION ---" << endl << endl;
printType(42); // Uses generic
printType(3.14); // Uses generic
printType("Hello"); // Uses const char* specialization
printType(true); // Uses bool specialization
cout << "\nCompare:" << endl;
cout << "compare(5, 10) = " << compare(5, 10) << endl;
// ⚠️ LEARNING NOTE: The original code had an error!
// compare("apple", "banana") fails because:
// - "apple" is char[6] (5 letters + null terminator)
// - "banana" is char[7] (6 letters + null terminator)
// Template deduction sees different types and fails!
//
// FIX: Use std::string or ensure same-length strings
cout << "compare(string(\"apple\"), string(\"banana\")) = " << compare(string("apple"), string("banana")) << endl;
cout << "compare(string(\"zebra\"), string(\"apple\")) = " << compare(string("zebra"), string("apple")) << endl;
cout << endl;
// ========================================================
// DEMO 5: Template Overloading
// ========================================================
cout << "--- DEMO 5: TEMPLATE OVERLOADING ---" << endl << endl;
process(42); // Calls non-template int overload
process(3.14); // Calls template
process("hello"); // Calls template
int val = 100;
process(&val); // Calls pointer template
cout << endl;
// ========================================================
// DEMO 6: Type Traits / SFINAE
// ========================================================
cout << "--- DEMO 6: TYPE TRAITS ---" << endl << endl;
cout << "numericOnly(10) = " << numericOnly(10) << endl;
cout << "numericOnly(3.5) = " << numericOnly(3.5) << endl;
// numericOnly("hello"); // Would fail to compile - not arithmetic
cout << endl;
// ========================================================
// DEMO 7: Variadic Templates
// ========================================================
cout << "--- DEMO 7: VARIADIC TEMPLATES ---" << endl << endl;
cout << "printAll(1, 2.5, \"three\", 'X'): ";
printAll(1, 2.5, "three", 'X');
cout << "sumAll(1, 2, 3, 4, 5) = " << sumAll(1, 2, 3, 4, 5) << endl;
cout << "sumAll(1.1, 2.2, 3.3) = " << sumAll(1.1, 2.2, 3.3) << endl;
cout << endl;
// ========================================================
// TEMPLATE SUMMARY
// ========================================================
cout << "--- FUNCTION TEMPLATE SYNTAX ---" << endl << endl;
cout << "Basic syntax:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "template <typename T>" << endl;
cout << "T funcName(T param) { ... }" << endl;
cout << "\nMultiple parameters:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "template <typename T1, typename T2>" << endl;
cout << "auto func(T1 a, T2 b) { ... }" << endl;
cout << "\nNon-type parameters:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "template <typename T, int SIZE>" << endl;
cout << "void func(T (&arr)[SIZE]) { ... }" << endl;
cout << "\nSpecialization:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "template <>" << endl;
cout << "void func<SpecificType>(...) { ... }" << endl;
cout << "\nVariadic:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "template <typename... Args>" << endl;
cout << "void func(Args... args) { ... }" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "FUNCTION TEMPLATES COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Create a template function findInArray that:
* - Takes an array and value to find
* - Returns index or -1 if not found
* - Specialize for C-strings using strcmp
*
* 2. Create a template function printContainer that:
* - Works with any container (vector, array, list)
* - Uses iterators
* - Prints elements separated by commas
*
* 3. Create variadic template functions:
* - maxAll() - returns maximum of all arguments
* - makeString() - concatenates all arguments
*
* 4. Create template with type traits:
* - average() that only works with numeric types
* - Uses static_assert or SFINAE
*/