C++ Function Templates with Examples

By , last updated October 3, 2019

In this tutorial we will give an introduction to C++ function templates and provide simple function template examples and a more complex sort predicates example with generic sorting template function. And to the end, there are some examples on C++ template function specialization.

You’re given the task of creating a method called is_even. This method will accept a number as input, and the result is true if the number is even.

The basic implementation will accept a number, and if successful return true. The most common way of checking if a number is even is to use the modulus operator. The modulus operator will return the remainder from a division 3 % 2 = 1, while 4 % 2 = 0. We can exploit this fact when implementing this method.

A very basic implementation will look like this:

bool is_even(int a)
{
    return (a % 2) == 0;
}

Calling this function with other types is allowed, but most compilers will issue a warning if you’re trying to call this method with a larger type than int. This code will issue a warning with warning level 2 (/W2) in Visual Studio and with -Wconversion with GCC.

#include <cstdint>

int64_t i64_val = 2;
is_even(i64_val);

// In Visual Studio:
// warning C4244: 'argument': conversion from 'int64_t' 
// to 'int', possible loss of data

// With GCC
// warning: conversion to 'int' from 'int64_t 
// {aka long int}' may alter its value [-Wconversion]

Binary representation of numbers in a computer

Some will argue this is a bad way of checking even-ness in a number. The modulus is per definition the remainder of a division operating, and division is much slower than addition and subtraction, and those are slower than checking bitness. Yes, it’s possible to check the LSB (least significant bit) on the number with 2 & 1 == 0. However, that is only true if and only if the number is an unsigned integer and/or the negative values are in two’s complement. With one’s complement, negative values will give the opposite result.

Value One’s complement Two’s complement
0 0000 0000
1 0001 0001
-1 1110 1111
2 0010 0010
-2 1101 1110

Table: 4-bit values in binary representation

As a C++ programmer, you can not assume your code will always execute on a two’s complement computer. It might happen your code will run on a totally different architecture, and this check will fail.

Using % 2 == 0 has no performance implications. It will be optimized away even in debug (by any compiler worth its salt).

Simple Function Template Example

With the previous detour in mind, the best and safe way of checking for a number to be even is to use % 2 == 0.

For an implementation accepting any number, of any type, the best way is to use templates. The benefits are tremendous, you write the algorithm, while the compiler has to figure out what type(s) to use.

Here is the answer:


template<typename T>
bool is_even(T val)
{
    return val % 2 == 0;
}

All types of numbers are accepted by this implementation. There are no warnings and everything just works.

int int_val = 2;
int64_t i64_val = (1LL << 33);

is_even(int_val);
is_even(i64_val);

This is what happens line by line. By starting the method with template, we’re letting the compiler know we want to use templates for this method. Next up is typename T. This means there is one type which can vary. The rest of the method is exactly like other non-template methods, with the notable exception of T instead of int. T is just a placeholder for the type the method is called with.

There are two ways of calling this templated method, either by implicit template deduction or explicit specification of the type. All methods are identical, unless there are non-template methods called is_even.

int val = 0;
is_even(val);       // Implicit template deduction
is_even<>(val);     // Implicit deduction
is_even<int>(val);  // Explicit template specification

Using <> or <int> will only try to look for a match in templated methods. There are many rules to overload resolution, but those are out of scope for this chapter.

Member of a Non-template Class Example

Function templates have two faces. Either they are a member of a non-template class, or they are free functions in the global or some namespace.

These are a member of a non-template class, with function templates. Here’s a simple example:

// filename: interface.hpp
class interface
{
protected:
    template<typename RESULTS, typename DATA>
    void insert(RESULTS &results, const DATA &data);
}
// filename: interface.cpp

#include "interface.hpp"

// Full specialization
template<>
void interface::insert<>( Results &results, const Data &data )
{
    // do stuff
}

Function Template Sort Example

Up until now there has been much theory, but not much practice. Here is a real world example on how to use function templates to sort anything that can be sorted.

// Generic function sort
#include <iostream>     // std::cout
#include <vector>       // std::vector
#include <algorithm>    // std::sort

// Predicate
template<typename T>
bool sort_predicate(const T & a, const T & b)
{
    return a < b;
}

template<typename T>
void generic_sort(std::vector<T> & arr)
{
    // Sorting the arr vector with predicate sort_predicate
    std::sort(arr.begin(), arr.end(), &sort_predicate<T>);
}

void test_function_sort()
{
    // Vector of numbers
    std::vector<int> numbers = { 3,6,4,2,5,7,1 };

    // Sort numbers
    generic_sort(numbers);

    // Print numbers
    for (int number : numbers)
    {
        std::cout << number << " ";
    }

    // Output is:
    // 1 2 3 4 5 6 7
}

If you need more functionality that would best fit as a Class which is parameterized by a type, you may consider creating a C++ Class Template.

Template specialization

Sometimes the generic implementation of templates doesn’t make any sense for a certain type, or it can be optimized to perform better with certain types.

For this we can use template specialization to specialize the implementation of both function templates and class templates.

Function template specialization

A C++ function template specialization specializes the primary template with a template function specialization. In other words, a template does something generic. A specialization makes the template function do something with a more specific way, and possibly more optimized than the generic case.

// Primary template
template<typename T>
void foo(T t);

The specialization looks like this.

// C++ template function specialization for int
template<>
void foo<int>(int t)
{
    std::cout << "foo<int>(int t) = " << t << "\n";
}

// C++ template function specialization for double
template<>
void foo<double>(double t)
{
    std::cout << "foo<double>(double t) = " << t << "\n";
}

When calling foo() with an int, the foo<int>-specialization will be selected. If the argument is a double, the foo<double> specialization will be selected.

foo(123);
foo(123.0);
foo((char)1);   // Linker error

If there are any other types, there will be a linker error if the specialization is not defined elsewhere in an other compilation unit.

LNK2019 unresolved external symbol "void __cdecl bar::foo<char>(char)" (??$foo@D@bar@@YAXD@Z) referenced in function main

To prevent the linker error if and only if no other types should be specialized, use a static_assert in the primary template body.

// Primary template with static_assert
template<typename T>
void foo(T t)
{
    static_assert(false, "Not to be used");
}

If C++17 is available, the terse version of static assert can be used, static_assert(message);

hello3.cpp(37): error C2338: Not to be used
hello3.cpp(66): note: see reference to function template instantiation 'void bar::foo<char>(T)' being compiled
    with
    [
        T=char
    ]

Senior Software Engineer developing all kinds of stuff.