Function Templates & Generic Programming

ফাংশন টেমপ্লেট

Read: ~35 min 18 practice problems

1. Write Once, Use With Many Types

tpl.cpp
#include <iostream>
#include <string>

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

int main() {
    std::cout << max(3, 5) << "\n";          // int
    std::cout << max(2.5, 3.5) << "\n";      // double
    std::cout << max(std::string{"abc"}, std::string{"abd"}) << "\n";
}
Template হলো compile-time generation। Compiler প্রতিটি ভিন্ন type T-এর জন্য একটি আলাদা version তৈরি করে।

2. Multiple Type Parameters

multi.cpp
template<typename A, typename B>
auto add(A a, B b) {
    return a + b; // return type deduced
}

// add(1, 2.5) -> double
// add(std::string{"hi"}, " there") -> std::string

3. Type Deduction Rules

  • By default, T is deduced as the value type (no const, no reference).
  • T& in the parameter preserves const.
  • const T& works with everything (lvalue or rvalue).
  • Arrays decay to pointers unless taken by reference.

4. Explicit Type Specification

If deduction fails or you want to force a type:

explicit.cpp
auto r = max<double>(3, 5); // force T = double

5. Template Definitions Live in Headers

The compiler must see the full template body to instantiate it. So template implementations go in the header file, not a .cpp.

6. Cost of Templates

  • Compile time: Each instantiation generates real code → bigger binaries, slower compile.
  • Runtime: Zero. The generated code is identical to hand-written code for that type.
  • Error messages: Historically cryptic. C++20 concepts fix this.

7. Practice Problems

  1. Write a generic swap function.
    ✨ Show Answer
    template<typename T>
    void swap(T& a, T& b) { T t = std::move(a); a = std::move(b); b = std::move(t); }
  2. Write a generic min/max.
    ✨ Show Answer
    template<typename T> T min(T a, T b) { return a < b ? a : b; }
    template<typename T> T max(T a, T b) { return a > b ? a : b; }
  3. Why is typename used in template<typename T>?
    ✨ Show Answer

    Both typename and class work — historical alternatives. They're equivalent. Modern style uses typename.

  4. What's a non-type template parameter?
    ✨ Show Answer
    template<int N>
    struct Array { int data[N]; };
    Array<100> a; // N=100 at compile time
  5. Generic print function.
    ✨ Show Answer
    template<typename T>
    void print(const T& x) { std::cout << x << "\n"; }
  6. Force type explicitly: max<long>(1, 2). Why might this matter?
    ✨ Show Answer

    To prevent narrowing or pick a specific overload. E.g. avoid int overflow in intermediate computations.

  7. A template that prints type size.
    ✨ Show Answer
    template<typename T>
    void printSize() { std::cout << sizeof(T) << "\n"; }
    // printSize<double>();  // 8
  8. Why must templates be in headers?
    ✨ Show Answer

    The compiler instantiates only when it sees both the template definition and the use. If the body is in a separate .cpp, the linker can't find the instantiation.

  9. A function template with a default type parameter.
    ✨ Show Answer
    template<typename T = int>
    T zero() { return T{}; }
    // zero();          // returns int 0
    // zero<double>();  // returns 0.0
  10. Generic sum over a vector.
    ✨ Show Answer
    template<typename T>
    T sum(const std::vector<T>& v) {
        T s{};
        for (const auto& x : v) s += x;
        return s;
    }
  11. Why are templates "zero-cost abstraction"?
    ✨ Show Answer

    Because the compiler generates type-specific code at compile time. There's no runtime overhead — equivalent to hand-writing maxInt, maxDouble separately.

  12. Define a fold-like template that applies a binary op.
    ✨ Show Answer
    template<typename Op, typename T>
    T reduce(const std::vector<T>& v, T init, Op op) {
        for (const auto& x : v) init = op(init, x);
        return init;
    }
  13. Print all elements of a container generically.
    ✨ Show Answer
    template<typename Container>
    void printAll(const Container& c) {
        for (const auto& x : c) std::cout << x << " ";
    }
  14. What does auto in a function parameter mean (C++20)?
    ✨ Show Answer

    An abbreviated function template. void f(auto x) is equivalent to template<typename T> void f(T x).

  15. Why might template error messages be cryptic?
    ✨ Show Answer

    The compiler instantiates and then reports errors deep inside template machinery. C++20 concepts fix this by stating constraints up front and giving clean errors.

  16. Use a concept to constrain T to be integral (C++20).
    ✨ Show Answer
    template<std::integral T>
    T square(T x) { return x * x; }
  17. Show that max(1, 2.5) fails to deduce.
    ✨ Show Answer

    Both args must agree on T. 1 is int, 2.5 is double — ambiguous. Either cast or use two type params.

  18. Specialize max for const char*.
    ✨ Show Answer
    template<>
    const char* max(const char* a, const char* b) {
        return std::strcmp(a, b) > 0 ? a : b;
    }

    Without this, max("a","b") compares pointers, not contents.

Summary

Function templates are compile-time code generation. They are zero-cost — the generated code is identical to hand-written. Templates live in headers. C++20 concepts make constraint checking and error messages much cleaner.

Next Module → Class Templates & Specialization.