From Java to C++ – Templates

Generic programming is everyday bread for possibly every Java programmer out there. Only the oldest Java programmers out there still remember the times, when we’re casting things all over, and lists were instantiated without explicitly telling the compiler what type of objects they hold. There should be no surprise that the same functionality exists in Java’s older cousin – C++.

DISCLAIMER! This article was written by me, but later, it was reviewed by an experienced C++ programmer/trainer. The parts where something was changed (or a comment was added), are written in blue. The conclusion is, that this post is quite trustworthy now 😉

DISCLAIMER 2! I didn’t want to go with full-blown history lesson here. I’m using the latest C++20 standard and all the examples are compiling and working in it. I may indicate once in a while when specific functionality was introduced.

Let’s go

Generic programming in the C++ is strictly related to the concept of a template. Although, in the Java world we’re used to the idea of generics, templates in C++ are a little different, and in my opinion, harder to grasp. In this article, I will try my best, to present generic programming in C++ in a way, that every Java programmer should easily understand. Let’s start with a definition of template, given by Stroustrup himself.

Basically, a template is a mechanism that allows a programmer to use types as parameters for a class or a function. The compiler then generates a specific class or function when we later provide specific types as arguments.

We can select three separate types of templates:

  • function templates
  • class templates
  • alias templates
  • variable templates (since C++14)

Additional typology comes with the template parameters (evaluated during compile time), in other words, what can be passed as a parameter to the template keyword:

  • param types – the most common ones, eg.
    template<typename|class T>
  • none-type parameters – usually already provided value, eg. number 6. Since C++20 it’s also possible to use floating-point numbers and C-style strings (arrays), although with some limitations. However, a lot of other types  can be used here, such as enums, pointers of all kinds or std::nullptr_t
  • template-template parametersstrong> – when we inject a template into another template

Here we must remember, that the T value, has its own scope, limited to the template only!

Function templates

This type of template should be very familiar to every Java programmer, as they look and work in the same way as generics in Java. We pass some generic class/type to the function, without any additional assumptions.

#include <iostream>
using namespace std;


template <typename T>  // T is usually used to indicate first param, U to indicate second one
T myMax(T x, T y)
{
    return (x > y) ? x: y;
}

// Based on the calls to this function in the main() method
// the template will be replaced with three separate implementations of 
// the above method using ints, doubles and chars as input params.

int main()
{
    short myShort = 2;
    cout << myMax<int>(3, 7) << endl; // Call myMax for int
    cout << myMax<int>(myShort, 7) << endl; // ERROR - this won't compile as the params are not of the same type 
    cout << myMax<double>(3.0, 7.0) << endl; // call myMax for double
    cout << myMax<char>('g', 'e') << endl; // call myMax for char
    
    return 0;
}

First thing to start with is – above template is just a recipe for creating concrete function definitions. The process of that creation is called template instantiation.

Second thing to discuss is whether we should use typename or class in the template definition. Well, as far as I’ve read, it does not matter that much (especially in C++20). Before the usages were somehow limited when template templates were involved. More on that can be found here.

Third important concept is – there’s no implicit conversion, when we’re using templates! That’s why in the above example the line using short variable failed miserably. In our case template expects both arguments to be the same type, and that’s it. No exceptions!

To overcome the above limitation is to use explicit template argument specification (yeah, I know, long name). It is an actual hint to the compiler what will be the type of the used variable. Let’s take a look at this example.

template<class T1, class T2>
void PrintNumbers(const T1&amp;amp; t1Data, const T2&amp;amp; t2Data)
{}

// This
PrintNumbers(10, 100);    // int, int
PrintNumbers(14, 14.5);   // int, double
PrintNumbers(59.66, 150); // double, int

// Can be changed to use this - and therefore limiting the generated specialized versions to only double one
PrintNumbers<double, double>(10, 100);    // int, int
PrintNumbers<double, double>(14, 14.5);   // int, double
PrintNumbers<double, double>(59.66, 150); // double, int

// Generated specialised version
void PrintNumbers<double, double>(const double&amp;amp; t1Data, const T2&amp;amp; t2Data)
{}

This technique should also be used, when there’s no way to figure out the type used from the params of the constructor or the methods. Consider this snippet of code:

template<class T>
void PrintSize()
{
    cout << "Size of this type:" << sizeof(T);
}

// Calling it like this will cause compiler error
PrintSize();

// But with this - it's working
PrintSize<float>();

Going back to the properties of function templates – third thing to mention is that function templates can be only put into global namespace or class scope. There’s no way to restrict their visibility to specified scope.

I’ve mentioned in the comments to the code above, that the compiler will create an actual code for every function template that’s used in the code. However, there’s a possibility to provide our own implementation of these functions if we want to (kind-of like default methods in Java interfaces). Take a look at the following code:

#include &lt;iostream&gt;
using namespace std;


template &lt;typename T&gt;
T myMax(T x, T y)
{
    cout &lt;&lt; "Inside template implementation: ";
    return (x &gt; y) ? x: y;
}

template&lt;&gt;  // <span style="color: rgb(0, 0, 255);" data-mce-style="color: #0000ff;">However, it may be easier to just override and skip using templates</span>
short myMax(short x, short y)
{
    cout &lt;&lt; "Inside short implementation: ";
    return (x &gt; y) ? x: y;
}

int main()
{
    cout &lt;&lt; myMax((short)3, (short)7 ) &lt;&lt; "\n" &lt;&lt; endl; // Call myMax for short
    cout &lt;&lt; myMax(3, 7) &lt;&lt; "\n" &lt;&lt; endl; // Call myMax for int
    cout &lt;&lt; myMax(3.0, 7.0) &lt;&lt; "\n" &lt;&lt; endl; // call myMax for double
    cout &lt;&lt; myMax('g', 'e') &lt;&lt; "\n" &lt;&lt; endl; // call myMax for char

    return 0;
}

Above technique is called specialization, and can be applied to every function template.

At the end of this section, it’s worth mentioning, that there’s a possibility to indicate to the compiler, a ‘default’ type in the template. I think that the following example will show what I mean.

template&lt;typename T, typename U = char&gt;
class A  {
    public:
        T x;
        U y;
        A() {   cout &lt;&lt; "Constructor Called" &lt;&lt; endl;   }
};

int main()  {
    A&lt;char&gt; a;  // This will call A&lt;char, char&gt;;
    return 0;
}

The output will be:

Constructor Called

Class templates

Class templates are working in a very similar way to the function templates, although they are defined at the class level (thank you, Captain Obvious!). As an additional point, there’s a possibility to implement them on a class methods outside of the class body (this technique applies also to the definition of ‘normal’ functions). The only thing we have to do while implementing method functions outside the class, is to prefix the function with a class name. Here’s an example:

#include<iostream>
using namespace std;


template <typename T>
class MyClass
{
public:
    MyClass(T x, T y)
    {
        cout << "My class constructor: " + to_string(x) + " " + to_string(y) + "\n";
    }

    T myMethodOutsideOfClass();
};

template<typename T>
T MyClass<T>::myMethodOutsideOfClass()
{
    cout << "Inside short implementation \n";
    return 0;
}

int main()
{
    MyClass<int> myClass(1,2);
    myClass.myMethodOutsideOfClass();
    return 0;
}

HINT! As a Java guy I was wondering – what’s the reason to define function outside the class? It’s stupid and puts the class logic in two different places. Well, you are right, it is. However, coming from Java we’re used to the fact, that out JARs are packed with both interfaces and implementations. In C++ (and in C), there’s this whole concept of dynamic/static libraries, and a possibility to separate interface of the class using header files, and use only them during work/writing code. If we put the whole logic inside a class (while it serves also as an interface), with every change in the implementation, we got to recompile the code. With clear separation of class  declaration from its definition – we don’t have to. Check out this SO question for more.

There’s one catch in the above – the implementation of the template must still be available to the compiler at the time of header processing! That’s why in a multi-file projects, if we want to expose a header with class declaration, all the template-using functions must also be put in the same file!

When it comes to class templates, we don’t need to operate only on the class level. There’s also a possibility to  introduce another templating solution which it called method template (pay attention to the word method, not function). I think that the code will explain it best.

#include<iostream>
using namespace std;


template <typename T>
class MyClass
{
    public:
    MyClass(T x, T y)
    {
        cout << "My class constructor: " &lt;&lt; x &lt;&lt; " " &lt;&lt; y) &lt;&lt; "\n";
    }

    template<typename U>
    void myMethodOfClass(const U&amp;amp; arg)
    {
        cout << "Passed arg: " &lt;&lt; arg &lt;&lt; "\n";
    }
};

int main()
{
    MyClass<int> myClass(1,2);
    myClass.myMethodOfClass(5);
    myClass.myMethodOfClass(2.0f);
    return 0;
}

As we’re in the object-oriented language (more or less), we have to talk a little about inheritance too. In general, there’s no problem with inheriting class templates. The only limitation here, is that we have to explicitly tell the  compiler, when we want to use base-class functionality. As usual, here’s the code:

#include<iostream>
using namespace std;

template<typename T>
class Foo
{
public:
    void Func() {}
};

template<typename T>
class Bar : public Foo<T>
{
public:
    void BarFunc()
    {
        // Func();   This causes compilation error
        this->Func();
        Foo<T>::Func();
    }
};

int main()
{
    Bar<int> b{};
    b.BarFunc();
}

Alias templates

We already saw that templates are great tool to reduce code size. There’s another way in which templates can be useful this way – alias templates. As usual, the best way to describe this functionality is with the simple code snippet:

template <typename T, int Line, int Col>
class Matrix {
....
};

template <typename T, int Line>
using Square = Matrix<T, Line, Line>;    // That's the alias

template <typename T, int Line>
using Vector = Matrix<T, Line, 1>; 

// Code that uses above constructions
Matrix<int, 5, 3> ma;
Square<double, 4> sq;
Vector<char, 5> vec;

The solution is clear and readable. HINT! The idea of aliasing is not only related to the templates! It can be applied to any other type/definition!

Variable/Variadic templates

For Java programmers the concept should be pretty familiar – varargs used in the templates. That’s it. In C++ this vararg is called template parameter pack, when used in the strong>template, and function parameter pack. Here’s the code:

// Args as a type/name is completely arbitrary and can be anything
template<typename... Args>  // this is for the template

void printMyParams(Args... args)  // this is for the function

I don’t want to dive into the concept of parameter pack right now. The best resource to get an understanding how it works can be found here. What I want to show is to how can they be used in the templates.

#include<iostream>

template <typename T, typename... Args>
T sum(T t, Args... args) {
    // [if constexpr] is evaluated during compile time
    // sizeof... is a special operator to determine the amount of params passed
    if constexpr(sizeof...(args) > 0) {  
        return t + sum(args...);
    } else {
        return t;
    }

    <del><span style="color: rgb(0, 0, 255);" data-mce-style="color: #0000ff;">return t;</span></del><span id="_mce_caret" data-mce-bogus="1" data-mce-type="format-caret"><span style="color: rgb(0, 0, 255);" data-mce-style="color: #0000ff;"> // It seems that in C++ it's better to ues ELSE in this case</span></span>
}

int main()
{
    std::cout << sum(1,2,3);
}

Executing this program will result in printed 6. In order for our example to work, an implementation of

double sum(T t)

should be provided. We avoid the need for that, by checking the amount of variable params passed to the function. As long as it is more than 0, we recursively call the function. If there’s no additional params to sum, we just return the passed value. Here is the code with a separate function written.

#include<iostream>

template<typename T>
T sum(T t)
{
    return t;
}

template<typename T, typename... Args>
T sum(T t, Args... args) {
    return t + sum(args...);
}

int main()
{
    std::cout << sum(1,2,3);
}

To sum up the presentation of parameter packs, I’ll show something, that is available since C++17 – folding (concept known from the functional languages) – if you’re interested you can read about it on the official page.

#include<iostream>

template<typename... Args>
auto sum(Args... args) {
    return (... + args);
}

int main()
{
    std::cout << sum(1,2,3);
}

Static template class members

Every function or class can contain static members. The wide question to ask would be – how do they behave when they’re used in the templates? The answer lies in the statement presented in the previous sub-chapter – templates are a recipe for creating specific functions or classes. Therefore, for every calculated combination of the types, a  separate instance of function/class will be generated by the compiler. Therefore, every instance will have its own, private static member.

Concepts

In this article I will just mention them. First – because they deserve more descriptive article, than just subchapter here. Second – it’s a new feature, available in C++20. Therefore, it’s not widely used yet. In general – in Java we write something like this:

class MyClass<T extends SomeOtherClass>() {
    T someOtherClass;
}

At the compilation level we check, whether passed object meets the criteria of extends SomeOtherClass. It’s a very powerful tool, and that’s why C++ introduced it too. However, as I’ve said, I will write a separate article later, and link it here.

SOURCES:

You Might Also Like

One Comment

  1. Tour of C++ review – Bare.Metal.Dev

    […] templates. I know it’s not that easy topic. However, looking at it from the perspective of written article about them, I can see clearly how badly was this chapter written. Unfortunately, later it’s not getting […]

Leave a Reply

Back to top