Lab 9 — More C++ programming

Template for Lab 9. More programming in C++.

Generic programming

First, a brief aside to Python. What things can you pass into this function without getting an error?

def square(n):
    return n * n

What about this one?

def add_twice(n):
    return x * x;

To square, you can pass any integer or any floating point number. To add_twice you can pass integers and floats, but also strings as addition between strings is defined as well. This is called duck typing—if the thing passed to square looks and quacks like a thing that can be multiplied, Python allows it, and if the thing passed to add_twice looks and quacks like a thing that can be added, Python allows that too.

This concept is related to generic programming, the ability to write code that works for multiple different data types instead of needing to duplicate functions for every data type.

Templates

In languages that have types such as C, C++, and Java, the type of an object passed to a function must match that function's type signature, the types of its arguments. For example, one way to implement square in C++ would be to write functions for each type we want to square:

float square_floats(float x) {
    return x * x;
}

int square_ints(int x) {
    return x * x;
}

Matrix square_matrices(const Matrix &x) {
    return x * x;
}

In all three of the above functions, the code is exactly the same. To get around this, C++ introduces templates, which take a type parameter and allow you to pass any type in that would fit. In this case, any type that has multiplication defined can be passed to this version of square:

template <typename T>
T square(const T &x) {
    return x * x;
}

Here, we use the type parameter T to stand in for a type we want to pass, such as int, float, or Matrix. At compile time, this function is used as a template to expand it into different functions for each type that is passed in, hence the name.

The above was an example of a templated function. You can also use templates for classes. In fact, if you've used std::vector, you've already used a templated class: you pass in the type that you want the vector to store in angle brackets, and you get a vector capable of storing that type back out. To create a class accepting a template, you write the the template signature above the class like so:

template <typename T>
class MyList {
public:
    MyList();
    ~MyList();

    size_t length() const;

    // functions that use the type parameter:
    T get(size_t index) const;
    void set(size_t index, const T &value);
private:
    // member variables that use the type parameter:
    T *_list;      /* pointer to an array of type `T` */
    size_t _slots; /* occupied slots in the list */
    size_t _count; /* total length of the list */
};