Skip to main content

    Lesson 11 • Expert

    Templates

    By the end of this lesson you'll be able to write one piece of code that works with any type — function templates, class templates, multiple type parameters, defaults, and a first taste of specialization — the exact technique the entire C++ Standard Library is built on.

    What You'll Learn

    • Write function templates with template <typename T>
    • Let template type deduction pick the type for you
    • Build class templates like Box<T> and Pair<K, V>
    • Use multiple type parameters and default template args
    • Write a basic template specialization for one type
    • Understand why template errors appear at instantiation

    💡 Real-World Analogy

    A template is a cookie cutter. The cutter defines the shape; the dough is the type. Press it into sugar dough and you get a sugar cookie; press it into gingerbread and you get a gingerbread cookie — same shape, different material. You write the shape once (template<typename T>) and the compiler stamps out a concrete version for every type you actually use. You don't get an int version until you press the cutter into an int.

    1. Function Templates & Type Deduction

    Without templates you'd copy maxOf once per type: one for int, one for double, one for string. A function template replaces all of them with a single recipe. You write template<typename T> above the function, then use T wherever a type would go. T is a placeholder: the compiler stamps out a real version the moment you call it. And thanks to type deduction, you usually don't even name the type — the compiler reads your arguments and works T out for itself.

    Worked example: a generic maxOf

    Read every comment, run it, and check the output matches.

    Try it Yourself »
    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    // A function template = one recipe the compiler stamps out per type.
    // "template <typename T>" introduces T, a placeholder for ANY type.
    template <typename T>
    T maxOf(T a, T b) {
        return (a > b) ? a : b;   // works for anything that supports >
    }
    
    int main() {
        // TEMPLATE TYPE DEDUCTION: you don't write the type — the compiler
        // looks at the arguments and figures T out for you.
        cout << maxOf(10, 20) << endl;        // 2
    ...

    Your turn. The program below is almost complete — write the template<typename T> line and the comparison so minOf returns the smaller value. Fill in the two blanks marked ___ using the hints, then run it.

    🎯 Your turn: write a generic minOf

    Add the template line and the body, then check your output against the expected lines.

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    // 🎯 YOUR TURN — write your own generic function template.
    
    // 1) Declare the template parameter on the line below.
    ___                              // 👉 template <typename T>
    
    // 2) Make minOf take two T's and return the SMALLER one.
    T minOf(T a, T b) {
        return ___;                  // 👉 (a < b) ? a : b
    }
    
    int main() {
        cout << minOf(10, 20) << endl;       // expect 10  (T = int)
        cout << minOf(3.5, 1.2) << endl;     // expect 1.2 (T = doubl
    ...

    2. Class Templates, Multiple Params & Defaults

    Templates aren't just for functions — a class template makes a whole class generic. Box<T> can hold an int, a string, or anything else. You can declare multiple type parameters too: Pair<K, V> has two independent placeholders. And a default template argument (typename V = int) lets callers leave a parameter off and get a sensible default — just like a default function argument. One gotcha: class templates need the type written in <...> when you create an object; deduction only happens automatically for function templates.

    Worked example: Box<T> and Pair<K, V>

    See a generic container, two type parameters, and a default argument in action.

    Try it Yourself »
    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    // A CLASS template — a generic container. T is filled in per object.
    template <typename T>
    class Box {
        T value;                              // a Box of "whatever T is"
    public:
        Box(T v) : value(v) {}
        T get() const { return value; }
        void set(T v) { value = v; }
    };
    
    // MULTIPLE TYPE PARAMETERS — two independent placeholders, K and V.
    // V has a DEFAULT TEMPLATE ARGUMENT: if you omit it, V becomes int.
    template <typename K
    ...

    Now you try. Creating an object from a class template means putting the type inside the angle brackets. Fill in the two blanks so price is a Box of double and name is a Box of string:

    🎯 Your turn: instantiate Box<T>

    Put the right type in the angle brackets, then run it.

    Try it Yourself »
    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    template <typename T>
    class Box {
        T value;
    public:
        Box(T v) : value(v) {}
        T get() const { return value; }
    };
    
    int main() {
        // 🎯 YOUR TURN — instantiate Box with the right types.
    
        // 1) A Box that holds a double set to 9.99
        Box<___> price(9.99);        // 👉 the type goes in the < > brackets
    
        // 2) A Box that holds a string set to "C++"
        ___ name("C++");             // 👉 Box<string> name("C++");
    
        cout
    ...

    3. Specialization & Where Errors Appear

    Sometimes the general template is wrong for one specific type. Template specialization lets you hand-write a dedicated version for exactly that type, using template<>. The classic case is comparing C-strings: the general > compares pointer addresses, not the text, so you specialize to compare the real characters. This section also shows the single most confusing thing about templates: errors appear at instantiation, not at definition. A template you never call is never type-checked — the compiler only complains when you actually use it with a type that doesn't fit.

    Worked example: specialization & instantiation errors

    Compare the general template with a const char* specialization, and see where errors surface.

    Try it Yourself »
    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    // The GENERAL template: maxOf works for any type with >.
    template <typename T>
    T maxOf(T a, T b) {
        return (a > b) ? a : b;
    }
    
    // A FULL SPECIALIZATION for const char* (C-strings). Without it, > would
    // compare POINTER addresses, not the text. "template <>" means: here is a
    // hand-written version just for this one type.
    template <>
    const char* maxOf<const char*>(const char* a, const char* b) {
        return (string(a) > string(b)) ? 
    ...

    🔎 Deep Dive: typename vs class

    You'll see both template<typename T> and template<class T> in real code. For declaring a type parameter they mean exactly the same thing. Modern code prefers typename because the placeholder doesn't have to be a class — it can be int, double, a pointer, anything.

    template <typename T> T id(T x) { return x; }  // recommended
    template <class T>    T id(T x) { return x; }  // identical meaning

    Pro Tips

    • 💡 Keep template code in headers: the compiler needs the full body to generate each type's version, so definitions live in .h/.hpp, not .cpp.
    • 💡 Let deduction work: write maxOf(5, 3), not maxOf<int>(5, 3) — only add explicit <...> when the compiler can't deduce or you want a specific type.
    • 💡 The STL is templates: vector<T>, map<K,V>, and sort() are all templates — everything you learn here you'll reuse constantly.
    • 💡 Read template errors top-down: find the first error and the call site that triggered it; the rest is usually noise.

    Common Errors (and the fix)

    • "undefined reference" linker error: you split a template into a .cpp file. Template definitions must be visible where they're used — put the whole template in the header.
    • Long, cryptic instantiation errors: templates are only checked when used, so the error explodes deep inside generated code. Read from the top, fix the first error, and look at the call that instantiated it.
    • "no matching function" on missing typename: when a dependent name is a type you must say so, e.g. typename vector<T>::iterator it; — forgetting typename there is a classic compile error.
    • "could not deduce template argument 'T'": you called maxOf(5, 3.14) — one int, one double, so T can't be both. Make the types match or force it: maxOf<double>(5, 3.14).
    • "no operator> for type X": you used a template with a type that doesn't support the operation inside it (comparing incompatible Ts). Use a type that defines the operator, or specialize the template for that type.

    📋 Quick Reference

    TaskCode
    Function templatetemplate <typename T> T id(T x);
    Call (deduced)maxOf(3, 7)
    Call (explicit type)maxOf<double>(3, 7)
    Class templatetemplate <typename T> class Box { ... };
    Instantiate classBox<int> b(42);
    Multiple paramstemplate <typename K, typename V>
    Default argtemplate <typename V = int>
    Specializationtemplate <> T maxOf<T>(...) { ... }

    Frequently Asked Questions

    Q: What's the difference between typename and class in a template?

    Nothing, for declaring a type parameter. template <typename T> and template <class T> are identical. typename is the more modern, clearer choice because the placeholder doesn't have to be a class — it can be int, double, or any type.

    Q: Why must template definitions go in header files?

    The compiler can only generate the int, double, or string version of a template when it can see the full template body at the point you use it. If the body is hidden in a .cpp file, other files get a 'undefined reference' linker error. Keep template code in the header (.h/.hpp).

    Q: Why are template error messages so long and cryptic?

    A template is only type-checked when it's instantiated (used) with a concrete type, so the error appears deep inside the generated code, not at the template definition. Read from the top, find the first error, and look at the call site that triggered it — that's usually the real culprit.

    Q: Does the compiler deduce types for class templates too?

    Function templates have always deduced types from their arguments (maxOf(10, 20) needs no <int>). Class templates required you to spell out Box<int> until C++17 added CTAD (class template argument deduction), which lets you write Box b(42); in modern code. This lesson spells the types out for clarity.

    Q: When should I write a template specialization?

    When the general template would behave wrongly or inefficiently for one specific type. The classic example is comparing C-strings (const char*): the general > compares pointer addresses, so you specialize to compare the actual text instead.

    Mini-Challenge: a generic clamp()

    No blanks this time — just a brief and an outline. Write a function template clamp that pins a value between a low and high bound, then prove it's generic by calling it with both ints and doubles. Check your output against the example in the comments.

    🎯 Mini-Challenge: build a generic clamp

    Write the template yourself and call it with two different types.

    Try it Yourself »
    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    // 🎯 MINI-CHALLENGE: a generic clamp()
    // 1. Write a function template "clamp" with one type parameter T.
    // 2. It takes (T value, T low, T high) and returns:
    //      low   if value < low
    //      high  if value > high
    //      value otherwise
    // 3. In main, call it with ints AND doubles to prove it's generic.
    //
    // ✅ Expected (clamp(15, 0, 10)=10, clamp(-3, 0, 10)=0, clamp(2.5, 1.0, 5.0)=2.5):
    //    10
    //    0
    //    2.5
    
    int main() {
      
    ...

    🎉 Lesson Complete

    • template<typename T> makes a function generic; T is a type placeholder
    • ✅ Type deduction lets the compiler pick T from your arguments
    • ✅ Class templates (Box<T>) make whole classes generic
    • ✅ Use multiple params (Pair<K, V>) and default args (V = int)
    • ✅ Specialize with template<> when one type needs different behaviour
    • ✅ Template errors appear at instantiation — read them top-down
    • Next lesson: the Standard Template Library — containers and algorithms built entirely on templates

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service