C++ Templates

In my previous article i wrote on function templates, Now i'm going to be writing on Class templates in C++.

WHAT ARE CLASS TEMPLATES?

Class Template can be referred to as blueprints that enable us to create a kind of specialized class with generic constructors, methods, destructors and more. We have a class Student:

class Student{
private:
    std::string temp;
    int data;
public:
    Student(std::string t, int d){
        temp = t;
        data = d;
    }
    int getdata(){
        return data;
   }
   std::string getstring(){
        return temp;
  }
};

and we want to model another class that's just like this one but has a string and a double as it's data members, normally we could just write another class with the same content but with a different name and change the int to double but that can get tedious and lengthy especially when it's a class with lots of content and it can make your code look unappealing, but class templates make things so much easier because we can write just one class that will serve as a template if we need a similar class, lets do that with the Student class:

#include <iostream>

template<typename S>
class Student{
    template<typename T> //this is for the overloading of the << operator
    friend std::ostream &operator<<(std::ostream &os, const Student<T> &rhs);
private:
    std::string temp;
    S data;
public:
    Student(std::string t, S d){
        temp = t;
        data = d;
    }
    S getdata() const{
        return data;
   }
   std::string getstring() consr{
        return temp;
  }
};
template<typename S> //another template is needed since the ones above can't be reused
std::ostream &operator<<(std::ostream &os, const Student<S> &rhs){
       os << "Name: " << rhs.getstring() << " Data: " << rhs.getdata();
       return os;
}

I overloaded the stream insertion << operator so i can output Student objects easier

I suggest you read the former article on function templates before this one

We create a template parameter template<typename S> that will be used to make the data attribute in the class able to model any data type, so we replaced the int with our template parameter S and that's basically it, now we can create a Student object that will have the ability to have a string with a double, float, other data types, now we create a Student object like this:

int main(){
    Student<double> padawan{"Padawan", 3.6};
    std::cout << padawan.getdata() << std::endl; //3.6
    Student<std::string> erik{"erik", "human"};
    std::cout << erik.getdata() << std::endl; //human
    Student<Student<int>> casey{"casey", {"glory", 16}};
    std::cout << casey.getstring() << std::endl; // casey
    std::cout << casey.getdata() << std::endl; // Name: glory Data: 16
    //or
    std::cout << casey.getdata().getstring() << std::endl; // glory
    std::cout << casey.getdata().getdata() << std::endl; // 16
    return 0;
}

Now you can see how easy it was for me to create a Student object that could model a string and other data types, you can make the two attributes to be templates, i.e X temp and S data but you would have to add that to the template parameter template<typename X, typename S> so the compiler knows what X is. Notice that i even modeled a Student object with a string and another Student object in it, if you can utilize it these templates properly it can save you writing a lot of unnecessary code, Also the syntax we used in creating these Student objects might look familiar to when you create a vector object like std::vector<int> vec{1,2,3};, that's because vectors too are also template classes behind the scenes, the same goes for other C++ containers like deques, maps, links e.t.c. So that's it, this a just a basic look into class templates, it can get very complex when you mix in stuff like inheritance, polymorphism e.t.c. Here's the source code for what i showed you so far in case you want to copy it into your IDE:

#include <iostream>

template<typename S>
class Student{
template<typename T>
    friend std::ostream &operator<<(std::ostream &os, const Student<T> &rhs);
private:
    std::string temp;
    S data;
public:
    Student(std::string t, S d) :temp{t}, data{d}{
    }
    S getdata() const{
        return data;
   }
   std::string getstring() const{
        return temp;
  }
};
template<typename S>
std::ostream &operator<<(std::ostream &os, const Student<S> &rhs){
       os << "Name: " << rhs.getstring() << " Data: " << rhs.getdata();
       return os;
}
int main(){
    Student<double> padawan{"Padawan", 3.6};
    std::cout << padawan.getdata() << std::endl; //3.6
    Student<std::string> erik{"erik", "human"};
    std::cout << erik.getdata() << std::endl; //human
    Student<Student<int>> casey{"casey", {"glory", 16}};
    std::cout << casey.getstring() << std::endl; // casey
    std::cout << casey.getdata() << std::endl; // Name: glory Data: 16
    //or
    std::cout << casey.getdata().getstring() << std::endl; // glory
    std::cout << casey.getdata().getdata() << std::endl; // 16
    return 0;
}

15