Consider the first example in the previous tutorial, where we created the Item, Weapon, and Armour classes. The player would need to have a way of obtaining these things, and one way of doing that is by buying them off a merchant with their hard-earned gold, no doubt pillaged from innumerable dead monsters. We might want to restrict each merchant to a single kind of thing, so we would create separate ItemMerchant, WeaponMerchant, and ArmourMerchant classes, each with an array of their corresponding objects that they can sell. Even if we create a base Merchant class for the common functionality and let the others inherit from it, we’ll still have to duplicate a fair bit of code. Instead, we can put C++’s templating system to good use by defining a single Merchant class that caters for all three inherited classes, and still restricts them each to a single inventory object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Define the base class
class Item
{
    /* Existing code */

    // New constructor
    Item() {}
};

// Weapon class inherits publicly from the Item class
class Weapon : public Item
{
    /* Existing code */

    // New constructor
    Weapon() : Item() {}
};

// Define a class template, where the identifier T is taken
// to be a different class in the following class definition.
// It doesn't matter which class yet, we'll define it later.
template <class T>
class Merchant
{
public:
    unsigned int current_stock;
    /* Create an array of 10 T objects. Because the array has to
       have all of its elements initialised the Merchant class
       needs to be able to call T's constructor, but Item and
       Weapon have different constructors so we define a new
       empty constructor that they both share. This is not the
       best solution, but it works and uses the tools we have
       available currently.*/
    T stock[10];

    Merchant()
    {
        current_stock = 0;
    }

    // T can be used anywhere a class name can, including in
    // function argument lists
    void add_item(T pItem)
    {
        if (current_stock < 9)
        {
            // The compiler doesn't know what T is, but both the
            // lhs and rhs are of type T so an assignment is allowed
            stock[current_stock] = pItem;
            ++current_stock;
        }
    }

    void list_items()
    {
        for(unsigned int i = 0; i < current_stock; ++i)
        {
            std::cout << i << ": ";
            /* We assume that T has a describe function, until we
                associate T with an actual class this is fine. If that
                class doesn't have a describe function, this line will
                cause a compile error */
            stock[i].describe();
            std::cout << std::endl;
        }
    }
};

int main()
{
    // Create a instance of the Merchant class and set the class T to be
    // the Item class. All instances of T in the definition of Merchant
    // will be replaced with Item
    Merchant<Item> item_merchant;
    // This is shorthand that saves creating a separate variable and instead
    // passes a new object to the function directly
    item_merchant.add_item(Item(1.0f, 100.0f));
    item_merchant.add_item(Item(2.0f, 50.0f));

    // Similarly but replacing T with Weapon
    Merchant<Weapon> weapon_merchant;
    weapon_merchant.add_item(Weapon(12.0f, 1024.0f, 24.0f));

    item_merchant.list_items();
    weapon_merchant.list_items();

    return 0;
}

Apologies for the length of the code snippet, it needed a lot of commenting! Using the template keyword we define the Merchant class to be a class template, which instead of defining a class defines an outline for what the class would be like, with T acting as a placeholder for a class (or a type such as int). Later in main we implicitly instantiate the template with Item and Weapon as arguments, creating two separate classes which we can then create instances of in the form of the item_merchant and weapon_merchant objects. Note that our class template accepts exactly one argument, and so any use of the Merchant class must too.

// This is not valid because Merchant is a class template
Merchant merch1;
// This is not valid because Merchant requires a class argument
Merchant<> merch2;
// This is not valid because Merchant should only have one argument
Merchant<Item, Weapon> merch3;

The third example can be valid however, because class templates can be defined to have any (fixed) number of arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T, class S>
class Foo
{
public:

    T t_var;
    S s_var;

    Foo(T t_var_p, S s_var_p)
    {
        t_var = t_var_p;
        s_var = s_var_p;
    }
};

Additionally a class instantiated from a class template is treated identically to a class that isn’t, including when used as an argument in a class template instantiation. So this is perfectly acceptable (and often useful)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

template <class T>
class Foo
{
public:
    T t;
    Foo() {}
    Foo(T t_p) { t = t_p; }
};

template <class S>
class Bar
{
public:
    S t;
    Bar() {}
    Bar(S t_p) { t = t_p; }
};

int main()
{
    // It's ugly, but it's correct!
    // int isn't a class but types are ok too so long as
    // they aren't used in any incorrect ways in the class template
    Foo<Bar<int>> var(Bar<int>(10));

    std::cout << var.t.t << std::endl;

    return 0;
}

Function Templates

Class template member functions can use the template arguments passed to their class, but if you just want to create a standalone function that can accept a variety of input classes then it’s a lot of effort to create an entire class just for that function, so instead you can use function templates, which work in a similar way to class templates but apply to single functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include "vector.hpp"

template <class T>
T sum(T values[], unsigned int n)
{
    // Assume n > 0, should check for this
    // We have to initialize, if we don't then s might
    // take a garbage value and this function won't work
    T s = values[0];
    for (unsigned int i = 1; i < n; ++i)
        s = s + values[i];
    return s;
}

// Explicitly instantiate the sum function template with
// an int argument. This is possible with classes too and has
// the same syntax
template int sum<int>(int values[], unsigned int n);

int main()
{
    const unsigned int n = 3;
    int v1[n] = {1, 3, 5};
    double v2[n] = {1.0, 9.0, 3.5};
    Vec3 v3[n] = {
        Vec3( 0, 1, 2),
        Vec3( 2,-1, 3),
        Vec3(-1, 0,-4) };
    // Call the sum function with an int argument
    std::cout << sum<int>(v1, n) << std::endl;
    // Implictly instantiate the sum function template with
    // a double argument and call it
    std::cout << sum<double>(v2, n) << std::endl;
    // Let the compiler deduce that the type is Vec3
    std::cout << sum(v3, n).mag() << std::endl;

    return 0;
}

The syntax for function templates is identical to that of class templates, but also allows for type deduction; if the type of an argument you are passing to a function template is clear, you do not need to include it in the argument list, allowing you to omit the list entirely as we’ve done here. You can still add empty <> if you want to make it clear that the function is an instantiation of a function template, if you wish.

It is important to note that templates will not be compiled if they are never used in the same file that they are defined (the compiler compiles each file individually so it doesn’t know about instantiations in other files). This means that if you declare and define a template in a .cpp and .hpp pair but do not explicitly instantiate it in them, then it will not be useable in any other .cpp file and you will get linker errors.

Specialisation

It is also possible to explicitly specialise a function template for a given class/type, in which case the specialisation overloads the function as normal. To do this, use the template keyword but don’t pass any arguments to it.

template<>
double sum(double values[], unsigned int n)
{
    double s = 0.0;
    for(int i = 0; i < n; ++i) s += values[i] * values[i];

    return s;
}

Now when sum is called with a double argument (explicitly or as deduced by the compiler) then this function—which sums the squares of the values—will be called instead.

Specialisation of class templates is also possible, in which case the the specialised class will be used when an instance of that class template is instantiated with the same arguments as the specialisation. It is also possible to specialise individual member functions of class templates, however this prevents you from specialising the entire class template.

Exercise

Modify the Vec3 class to be a class template, replacing the float type with a general one. When the type is of int, the mag function should return the manhattan length of the vector, defined by . (If x is an int then |x| is the positive version of x.) Remember to explicitly instantiate the Vec3 class for at least the int and double types! The following should work with your class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include "vector.hpp"

int main()
{
    Vec3<double> foo(2.0, 1.0, 2.0);
    Vec3<int> bar(1, 2, -3);

    // This should be 3
    std::cout << foo.mag() << std::endl;
    // This should be 6
    std::cout << bar.mag() << std::endl;

    return 0;
}
Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* vector.hpp */
#ifndef VECTOR_HPP
#define VECTOR_HPP

template <class T>
class Vec3
{
    public:

    T x, y, z;

    Vec3(T aX, T aY, T aZ)
    {
        x = aX;
        y = aY;
        z = aZ;
    }

    T mag();
};

// Scalar multiplication of T * Vec3
template <class T>
Vec3<T> operator*(T lhs, Vec3<T> rhs);

// Addition and subtraction of Vec3 and Vec3
template <class T>
Vec3<T> operator+(Vec3<T> lhs, Vec3<T> rhs);
template <class T>
Vec3<T> operator-(Vec3<T> lhs, Vec3<T> rhs);

// Take the square root of a positive number
template <class T>
T sqrt(T a);

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* vector.cpp */
#include "vector.hpp"

// We assume that T is a simple numeric type,
// if it isn't then we would have to write a specialisation
// as this code wouldn't work with it.
template <class T>
T sqrt(T a)
{
    T x = a / 2.0;

    if(-0.00001 < x && x < 0.00001) return 0.0;


    for(int i = 0; i < 10; ++i)
    {
        x += a / x;
        x *= 0.5;
    }

    return x;
}

template <class T>
Vec3<T> operator*(T lhs, Vec3<T> rhs)
{
    // Could create a new value and return it,
    // or since we aren't returning a reference we
    // can return the new object directly
    return Vec3<T>(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
}

template <class T>
Vec3<T> operator+(Vec3<T> lhs, Vec3<T> rhs)
{
    return Vec3<T>(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z);
}

template <class T>
Vec3<T> operator-(Vec3<T> lhs, Vec3<T> rhs)
{
    return Vec3<T>(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z);
}

template <class T>
T Vec3<T>::mag()
{
    // Only dealing with three components so we can simplify this
    return sqrt(x*x + y*y + z*z);
}

// Specialisation of mag to the int type
template <>
int Vec3<int>::mag()
{
    // Calculate the manhattan length of the vector
    int X = x, Y = y, Z = z;
    if(X < 0) X = -X;
    if(Y < 0) Y = -Y;
    if(Z < 0) Z = -Z;
    return X + Y + Z;

    /* The faster way is by using the ternary operator
       which evaluates to the value after the ? if the
       condition is true, and the value after the : if
       the condition is false. Hence
       x > 0 ? x : -x
       will evaluate to x if x > 0 and -x otherwise. */

    // return (x>0?x:-x) + (y>0?y:-y) + (z>0?z:-z);
}

// Explicit instantiations of Vec3 for float and double. Without
// these the program will fail to link
template class Vec3<float>;
template class Vec3<double>;