[C++]Modelling an Array of multiple types using Templates

[C++]Modelling an Array of multiple types using Templates

In my previous articles I talked about what templates in C++ are and it's types with examples. Here I'm going to show you a little power of templates, I'm going to model my own array which can take hold multiple types. First I'm going to model an array of int then turn it into an array that can conform to multiple types, Let's get started:

#pragma once
#include <iostream>
template<int S>
class Array{
    friend std::ostream &operator<<(std::ostream &os, const Array<S> &arr){ //OVERLOADED STREAM INSERTION OPERATOR
        os << "[ ";
        for(const auto &a: arr.data)
            os << a << " ";
        os << "]";
        return os;
    }
    int data[S];
    int size{S};
    static const int limit{};
public:
    Array() = default;
    Array(int ini = 0){
        for(auto &a: data)
            a = ini;
    }

    int get_size() const{ //RETURNS THE SIZE OF THE ARRAY
        return size;
    }

    void fill(int fil, int stop = limit){ //CHANGES ALL THE VALUE OF THE ARRAY UP TO A SPECIFIED POINT
        for(int i{0}; i <= stop; ++i)
            data[i] = fil;
    }

    void set(int index, int info){ //CHANGES THE VALUE OF THE ARRAY AT A PARTICULAR INDEX
        data[index] = info;
    }

    int &operator[](int a){ //OVERLOADED SUBSCRIPT OPERATOR
        return data[a];
    }
};

Let's understand what's going on in the code, i created a template which has an int S as it's template parameter which is what we use to represent the fixed size of an array, then i go ahead and create a class Array whose data members is:

  1. An array of int 'data' whose size is dependent on the value that S takes during the creation of the Array object
  2. An int 'size' which represents the size of the array and is initialized to the value of S
  3. A static const int 'limit' which i just use to give a particular function in the class more power, it's not really needed in the program

I have a constructor that initializes all the value in the array to whatever the user passed in, then a function that returns the size of the array, a fill function which expects two int values, the first being the value that will replace the ones already in the array and the second being a sort-of 'limiter' to the fill function by allowing the user to set the range which the fill function affects. I also have a set function which replaces the value of the array at a specified index and then i have two overloaded operators:

Subscript operator [] which lets us access the value of the array at a specified index.

Stream insertion operator << which lets us output an Array object easily.

Now we have a model of an Array of int and I've ran some tests to show you -

image.png Everything works fine, now we want to make this class able to model other data types without writing multiple classes for the different types, as usual we use templates to accomplish this:

#pragma once
#include <iostream>
template<typename T, int S>
class Array{
    friend std::ostream &operator<<(std::ostream &os, const Array<T, S> &arr){ //OVERLOADED STREAM INSERTION OPERATOR
        os << "[ ";
        for(const auto &a: arr.data)
            os << a << " ";
        os << "]";
        return os;
    }
    T data[S];
    int size{S};
    static const int limit{S};
public:
    Array() = default;
    Array(T ini = 0){
        for(auto &a: data)
            a = ini;
    }

    int get_size() const{ //RETURNS THE SIZE OF THE ARRAY
        return size;
    }

    void fill(T fil, int stop = limit){ //CHANGES ALL THE VALUE OF THE ARRAY UP TO A SPECIFIED POINT
        for(int i{0}; i <= stop; ++i)
            data[i] = fil;
    }

    void set(int index, T info){ //CHANGES THE VALUE OF THE ARRAY AT A PARTICULAR INDEX
        data[index] = info;
    }

    T &operator[](int a){ //OVERLOADED SUBSCRIPT OPERATOR
        return data[a];
    }
};

That's it, Let's walk through it. I've added another template parameter 'T' which represents the type that the array would represent, so we replace some places where int used to be with T since the type the array takes is dependent on what the user sets 'T' as, but we don't change int size and int stop = limit because they always has to be an integer obviously, we change the overloaded [] operator to return a reference to a T object rather than an int and the fill function now expects a 'T' object rather than an int because now the array doesn't have a definite type unlike before. Let's run some tests:

image_2021-04-05_235003.png

So that's about it, that's a simple way to model an array of multiple types using templates, it can be tweaked to do other things, other class functions can be added to make it do more and stuff, here's the test codes in case you want to copy into your IDE:

#include <Array.h>
#include <string>
using std::cout;
using std::cin;
using std::endl;

int main(){
    Array<double, 4> arr{5.4};
    cout << arr << endl;
    arr.fill(6.1, 2); //REMEMBER COUNTING STARTS FROM 0
    cout << arr << endl;
    arr.set(0, 9.7);
    cout << arr << endl;
    arr[1] = 2.3;
    cout << arr << "\n"<< endl;


    Array<std::string, 4> str{"Hi"};
    cout << str << endl;
    str.fill("Bye", 1);
    cout << str << endl;
    str.set(2, "Bad");
    cout << str << endl;
    str[3] = "Human";
    cout << str << endl;
    return 0;
}

If you have any questions, you can message me on twitter or on discord (Padawan #4324)