Programming Pandit

c/c++/c#/Javav/Python


Latest Update

Friday, October 23, 2026

Templates: Generic Functions

Templates: Generic Functions

 

Learning Objectives

After completing this module, students should be able to:

•      Explain why templates are needed and how the compiler instantiates a function template.

•      Write generic functions with one or more type parameters.

•      Apply generic functions using implicit deduction and explicit template arguments, and explain why deduction can fail.

•      Distinguish function templates from function overloading and use an explicit specialization.

•      Write practical generic functions (swap, sort, search) and recognise common mistakes.

 

1. Introduction to Templates

A function normally works with specific data types. A function that finds the larger of two int values cannot be used for two double values, so the same logic would have to be written again for each type:

int maximum(int a, int b)          { return (a > b) ? a : b; }

double maximum(double a, double b) { return (a > b) ? a : b; }

 

Both functions perform exactly the same operation; only the data type is different. C++ solves this duplication with templates.

Definition: A template is a mechanism that lets a programmer write a function or a class once, with the data type left as a parameter. The compiler uses the template, together with the types actually supplied, to generate the type-specific code during compilation.

 

Writing code that is independent of any particular data type is called generic programming. The C++ Standard Library (vector, list, map, sort, …) is built almost entirely from templates.

1.1 Advantages

1.      Reusability: one definition serves many types.

2.      Less duplication and easier maintenance: a correction is made in one place.

3.      Type safety: the generated functions are checked by the compiler, unlike macros or void *.

4.      Efficiency: templates are resolved at compile time, so there is no run-time type test.

 

Important: A template does not mean that every operation works with every type. The type supplied must support the operations used in the template body; for example, a > b needs a suitable operator>.

 

2. Generic Functions

A generic function is a function that can operate on different data types without a separate definition for each. In C++ it is written as a function template.

2.1 Syntax

template <typename T>

return_type function_name(T parameter1, T parameter2)

{

    // function body

}

 

Component

Meaning

template

Keyword that starts a template declaration

<typename T>

Template parameter list: T is a type parameter (a placeholder for a type)

return_type

Return type; it may be T, another type, or auto (see Section 5.3)

T parameter1

A function parameter whose type is the type argument

 

T is not a built-in type; it is a name for a type that is chosen when the function is used. Any valid identifier may be used instead of T. For a type parameter the keywords class and typename mean the same:

template <class T>

T square(T value) { return value * value; }

 

Version Note: Function templates have been part of C++ since the first standard (C++98). The keyword typename was added so that it can also be used in other contexts (Module 2, Section 8); in a template parameter list class and typename are interchangeable in every standard.

 

3. How a Function Template Works

A function template is not a function; it is a pattern from which functions are generated. When a call such as maximum(10, 20) is compiled, the compiler deduces T = int and generates (instantiates) the function int maximum(int, int). A call with double arguments generates a second function, and so on. A function is generated only for the types that are actually used.

Title: Instantiation of a function template: one definition, one generated function per type used. - Description: Instantiation of a function template: one definition, one generated function per type used.

Figure 1.1: Instantiation of a function template: one definition, one generated function per type used.

•      Instantiation happens at compile time; the program contains the generated functions.

•      The body is checked for each type: if the type does not support an operation, a compile-time error is reported for that instantiation.

•      Many different types lead to many generated functions, which can increase the size of the executable (code bloat).

 

4. Example: A Generic Maximum Function

Program 1.1: Finding the maximum of two values of any comparable type.

#include <iostream>

#include <string>

using namespace std;

 

template <typename T>

T maximum(T a, T b)

{

    return (a > b) ? a : b;

}

 

int main()

{

    cout << "Maximum integer  : " << maximum(10, 20) << endl;

    cout << "Maximum double   : " << maximum(5.5, 2.3) << endl;

    cout << "Maximum character: " << maximum('A', 'Z') << endl;

    cout << "Maximum string   : " << maximum(string("apple"), string("banana"))

        << endl;

    return 0;

}

 

Output:

Maximum integer  : 20

Maximum double   : 5.5

Maximum character: Z

Maximum string   : banana

 

Explanation

1.      template <typename T> declares T as a type parameter.

2.      T maximum(T a, T b) takes two values of the same type T and returns the larger one using the conditional operator.

3.      In maximum(10, 20) both arguments are int, so T = int. With 5.5 and 2.3, T = double; with 'A' and 'Z', T = char.

4.      For char the comparison uses the character codes; in ASCII-compatible encodings 'Z' (90) is greater than 'A' (65).

5.      A string works too, because std::string defines operator>.

 

5. Applying Generic Functions

To apply a generic function means to call it so that the compiler can select or generate the right type-specific function. There are two ways.

5.1 Implicit deduction and explicit template arguments

•      Implicit (template argument deduction): the compiler deduces T from the types of the call arguments: square(5) gives T = int.

•      Explicit: the programmer writes the type in angle brackets: square<double>(5) forces T = double; the argument 5 is converted to double.

 

Explicit arguments are needed when the type cannot be deduced (for example, when it appears only in the return type) or when a particular type is wanted.

Program 1.2: Implicit and explicit template arguments, and the effect of mixed types.

#include <iostream>

using namespace std;

 

template <typename T>

T square(T value)

{

    return value * value;

}

 

template <typename T>

T add(T a, T b)

{

    return a + b;

}

 

int main()

{

    /* implicit deduction */

    cout << square(5) << endl;           // T = int

    cout << square(2.5) << endl;         // T = double

 

    /* explicit template arguments */

    cout << square<int>(5) << endl;

    cout << square<double>(2.5) << endl;

 

    /* add(10, 2.5) would fail: T cannot be both int and double */

    cout << add<double>(10, 2.5) << endl;    // explicit: 10 is converted to double

    cout << add(10, 20) << endl;             // T = int

    return 0;

}

 

Output:

25

6.25

25

6.25

12.5

30

 

The first two lines use deduction, the next two use explicit arguments, and the last two use add: add<double>(10, 2.5) gives 12.5, and add(10, 20) gives 30.

5.2 Why deduction can fail

Template argument deduction examines each argument and must arrive at one type for each template parameter. It does not apply the usual conversions to pick a common type.

Call

Deduced from

Result

add(10, 20)

T = int from both arguments

Valid

add(1.5, 2.5)

T = double from both

Valid

add(10, 2.5)

T = int from the first, T = double from the second

Error: conflicting deduction

add<double>(10, 2.5)

T given explicitly; 10 is converted

Valid: 12.5

 

There are three ways to correct a failing call such as add(10, 2.5): (1) give the type explicitly, add<double>(10, 2.5); (2) cast an argument, add(10.0, 2.5); or (3) use two template parameters, as in the next section.

5.3 Generic functions with multiple template parameters

template <typename T1, typename T2>

return_type function_name(T1 a, T2 b);

 

Now the arguments can have different types. The return type of such a function often depends on both types, so it is written with auto.

Program 1.3: Two type parameters, and a deduced return type.

#include <iostream>

using namespace std;

 

template <typename T1, typename T2>

void display(T1 a, T2 b)

{

    cout << "First value : " << a << endl;

    cout << "Second value: " << b << endl;

}

 

/* C++11: trailing return type */

template <typename T1, typename T2>

auto addOld(T1 a, T2 b) -> decltype(a + b)

{

    return a + b;

}

 

/* C++14: the return type is deduced from the return statement */

template <typename T1, typename T2>

auto add(T1 a, T2 b)

{

    return a + b;

}

 

int main()

{

    display(10, 3.14);

    display('A', 100);

    cout << add(10, 2.5) << endl;

    cout << add(5, 7) << endl;

    cout << addOld(1.5, 2) << endl;

    return 0;

}

 

Output:

First value : 10

Second value: 3.14

First value : A

Second value: 100

12.5

12

3.5

 

Version Note: C++11: the return type must be written explicitly with a trailing return type, as in auto addOld(T1 a, T2 b) -> decltype(a + b).

C++14 and later: plain auto is enough; the compiler deduces the return type from the return statement (function add in Program 1.3).

Compiled with -std=c++11, Program 1.3 is rejected:

 

p1_multi.cpp:20:1: error: ‘add’ function uses ‘auto’ type specifier without trailing return type

 

6. Function Templates and Function Overloading

Function overloading means several functions with the same name and different parameter lists, each written separately. A function template is one definition from which such functions are generated. The two can be mixed.

Basis

Function overloading

Function template

Definition

Several separate functions with one name

One generic definition

Parameter types

Written separately in every overload

Represented by template parameters

Code duplication

Higher: the body is repeated

Lower: written once

Behaviour for different types

May be completely different

Same algorithm for every type

Type support

Only the declared overloads

Any type that supports the operations used

 

6.1 How the compiler chooses between them

When an ordinary function and a template could both be called, overload resolution prefers the better match, and if the matches are equally good it prefers the ordinary (non-template) function.

Title: Order in which candidates are considered when a call is resolved. - Description: Order in which candidates are considered when a call is resolved.

Figure 1.2: Order in which candidates are considered when a call is resolved.

6.2 Explicit specialization

Sometimes the generic algorithm is wrong for one particular type. A specialization provides a special version of the template for that type. For example, maximum compares addresses, not text, when applied to C-style strings; a specialization for const char * compares the characters.

Program 1.4: Template, ordinary function with the same name, and an explicit specialization.

#include <iostream>

#include <cstring>

using namespace std;

 

/* Ordinary function */

int display(int x)

{

    return x + 100;

}

 

/* Function template with the same name */

template <typename T>

T display(T x)

{

    return x * 2;

}

 

/* Primary template */

template <typename T>

T maximum(T a, T b)

{

    return (a > b) ? a : b;

}

 

/* Explicit specialization for C-strings: compares the text, not the addresses */

template <>

const char *maximum<const char *>(const char *a, const char *b)

{

    return (strcmp(a, b) > 0) ? a : b;

}

 

int main()

{

    cout << display(10) << endl;        // ordinary function is preferred

    cout << display(2.5) << endl;       // template with T = double

    cout << display<int>(10) << endl;   // explicit <int> forces the template

    cout << maximum("apple", "banana") << endl;

    cout << maximum(3, 9) << endl;

    return 0;

}

 

Output:

110

5

20

banana

9

 

•      display(10) calls the ordinary function (110), because an exact non-template match is preferred.

•      display(2.5) calls the template with T = double (5).

•      display<int>(10) uses the explicit <int> to force the template (20).

•      template <> introduces the explicit specialization; for const char * it uses strcmp(), so maximum("apple", "banana") prints banana.

 

7. Practical Applications of Function Templates

Typical uses are: finding the maximum or minimum, swapping two values, sorting, searching, printing arrays, and writing reusable utility functions. Program 1.5 contains several of them.

Program 1.5: Generic swap, bubble sort, linear search, array printing and array size.

#include <iostream>

using namespace std;

 

template <typename T>

void swapValues(T &a, T &b)

{

    T temp = a;

    a = b;

    b = temp;

}

 

/* Generic bubble sort */

template <typename T>

void bubbleSort(T arr[], int n)

{

    for (int i = 0; i < n - 1; i++)

        for (int j = 0; j < n - 1 - i; j++)

            if (arr[j] > arr[j + 1])

                swapValues(arr[j], arr[j + 1]);

}

 

/* Generic linear search: returns the index or -1 */

template <typename T>

int linearSearch(const T arr[], int n, T key)

{

    for (int i = 0; i < n; i++)

        if (arr[i] == key)

            return i;

    return -1;

}

 

template <typename T>

void printArray(const T arr[], int n)

{

    for (int i = 0; i < n; i++)

        cout << arr[i] << " ";

    cout << endl;

}

 

/* The array size is deduced from the array type itself */

template <typename T, int N>

int arraySize(T (&)[N])

{

    return N;

}

 

int main()

{

    int a[] = {40, 10, 30, 20};

    double d[] = {2.5, 1.5, 3.5};

    char c[] = {'q', 'a', 'z', 'm'};

 

    bubbleSort(a, arraySize(a));

    bubbleSort(d, arraySize(d));

    bubbleSort(c, arraySize(c));

 

    printArray(a, arraySize(a));

    printArray(d, arraySize(d));

    printArray(c, arraySize(c));

 

    cout << "Index of 30: " << linearSearch(a, arraySize(a), 30) << endl;

    cout << "Index of 9 : " << linearSearch(a, arraySize(a), 9) << endl;

 

    int x = 10, y = 20;

    swapValues(x, y);

    cout << x << " " << y << endl;

    return 0;

}

 

Output:

10 20 30 40

1.5 2.5 3.5

a m q z

Index of 30: 2

Index of 9 : -1

20 10

 

•      swapValues(T &a, T &b) uses references, so the original variables are changed, not copies. Both parameters have the same type T.

•      bubbleSort(T arr[], int n) sorts an array of any type that supports > and assignment; the same code sorted int, double and char arrays.

•      arraySize(T (&)[N]) shows a non-type template parameter N: the compiler deduces the number of elements from the array type, so no size has to be passed.

 

Note: The Standard Library already provides std::swap, std::max, std::min and std::sort. In real programs these should be used. The programs here use different names (swapValues, maximum) on purpose: with using namespace std;, a user-defined template named swap can make the call ambiguous with std::swap and cause the error call of overloaded ‘swap(int&, int&)’ is ambiguous.

 

8. Rules, Limitations and Common Mistakes

1.      Required operations: every operation in the body must be valid for the supplied type.

2.      Deduction must succeed: with f(T, T) a call with an int and a double fails unless the type is given explicitly.

3.      Place the definition in a header file. The compiler needs the whole template body at the point where it instantiates it, so templates are normally defined in headers rather than in a separately compiled .cpp file (see the note about export in Module 2).

4.      Templates do not guarantee speed. They allow efficient type-specific code, but performance depends on the compiler and the algorithm.

5.      Error messages can be long because the error is reported where the template is instantiated.

 

Mistake

Why it is a problem

Correction

Using template but forgetting the parameter list

T is not declared

Write template <typename T> before the function

Calling f(10, 2.5) when both parameters are T

Conflicting deduction

Use explicit arguments, a cast, or two type parameters

Using an operator the type does not provide

Compile error at instantiation

Use a suitable type or overload the operator

Treating T as a real type name

T is only a placeholder

T stands for whichever type is chosen at instantiation

Defining a template only in a .cpp file

Other files cannot instantiate it (linker error)

Put the full definition in a header

Comparing C-strings with a generic >

Compares addresses, not text

Use a specialization or std::string

 

Version Note: C++20 concepts. The informal rule “the type must support the operations” can be written in the code itself as a constraint. If the type does not satisfy it, the compiler reports that the constraint is not satisfied, which is much clearer than a long instantiation error. This works only in C++20 (g++ -std=c++20).

 

#include <iostream>

#include <concepts>

using namespace std;

 

template <typename T>

requires totally_ordered<T>

T maximum(T a, T b) { return (a > b) ? a : b; }

 

 

int main()

{

    cout << maximum(3, 9) << endl;

    return 0;

}

 

9

 

9. Quick Revision

Concept

Key point

Template

Mechanism for generic programming; the compiler generates type-specific code

Generic function

A function template; one definition for many types

typename T / class T

Declare a type parameter (equivalent in a template parameter list)

Instantiation

Generation of a concrete function when a call needs it (at compile time)

Deduction

The compiler finds T from the call arguments; it must find one consistent type

Explicit argument

square<double>(2) specifies T directly

Several parameters

template <typename T1, typename T2> allows different argument types

Specialization

template <> gives a special version for one type

Overload preference

An exact ordinary function beats a template with the same match

 

10. Important Examination Questions

Short-answer questions

1.      Define a template. What is generic programming?

2.      What is a generic function? Write its general syntax.

3.      Differentiate between typename and class in a template parameter list.

4.      What is template argument deduction? When does it fail?

5.      What is an explicit template argument? Give an example.

6.      Can a function template take arguments of different types? Explain.

7.      What is an explicit specialization? Why is it needed?

8.      State four advantages and two limitations of templates.

 

Long-answer and programming questions

1.      Explain function templates with syntax and a complete C++ program.

2.      Write a generic function to find the maximum of two values and show how it is applied to int, double and char.

3.      Explain template argument deduction with examples of success and failure.

4.      Write a program using a function template with two type parameters.

5.      Differentiate between function overloading and function templates.

6.      Write a generic function to swap two variables of the same type. (A single template with T &a, T &b cannot swap an int with a double; this needs two parameters or a conversion.)

7.      Write a generic bubble sort and a generic linear search.

8.      Explain the limitations and common errors of function templates.

 


No comments:

Post a Comment