Previously we’ve looked at template functions and we’ve looked at template classes. This time, let’s look at what happens when you combine them.
Template member functions
A non-template class can have template member functions, if required.
Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class. In this case the compiler generates a member function for each (unique) template type. As before, the compiler will favour a non-template overload of a member function.
Template functions on template classes
What about if our class is itself a template? A template class will typically have member functions defined in terms of its own template parameter, but may equally have member functions that are themselves template functions. In this case the member function’s template parameter is different to the owning class’.
The syntax for specifying the template member function is rather obtuse (to say the least):
The containing class’ template declaration must be ‘outer’ one; func is a template function (typename U) that belongs to the class Utility<T> (typename T)
Notice that the template function must have a different template parameter identifier to its containing class (even if the same type is being used to instantiate both the class and the function).
A common application of template member functions on template classes is constructors; particularly if inheritance is involved. (Have a look here for more on template inheritance)
Let’s revisit a previous example.
Above is an example of parameterised inheritance. In the earlier article I (deliberately) ignored the construction of the base class objects. The problem we face is that we cannot determine the structure of the base class constructor – each base class constructor will have its own number of parameters, with different types – so how can we write a Named constructor that satisfies any potential base class?
Let’s split this problem into two: parameters of different types; and different numbers of parameters
We can deal with different parameter types by making the Named class constructor a template member function.
OK… deep breath… let’s wade through this mire of syntax and work out what’s happening.
The constructor for Named has three parameters: a string literal (const char*) and two template parameter arguments. Notice these parameters are passed as r-value references (Arg&&). In this situation Scott Meyers refers to these as Universal References – meaning “a reference that can bind to anything”.
In the body of the constructor, notice the use of the template function std::forward. This function ensures that the types of the parameters are forwarded on without changing their types – that is, l-values remain l-values, r-values remain r-values.
(I’m deliberately glossing over the details of these mechanisms here as they’re not the focus of this article. For a detailed description I highly recommend reading Meyers’ excellent article “Universal References in C++11”
Now when we construct a Named object, its constructor (function) can deduce the types of the supplied arguments and pass them on to the (template-parameter) base class.
So far, so good, but our Named constructor is still limited: It will only work for base classes that have exactly two parameters. If we try and use a class with three parameters we get a problem:
Our Lamp class is a candidate for being the Named base class, since it supports the display() method. However, its constructor requires three parameters – and our template constructor can only supply two.
The short-term fix is to overload the Named constructor to take three parameters:
This works; but it means we’ll have to overload the constructor for zero parameters, one parameter, two, three, four, etc. This can quickly become onerous, so in the next article we’ll have a look at a new mechanism in C++11 designed to make this more flexible – Variadic Templates.
Template member functions allow us to parameterise functions independently of the class they belong to. They can be added to both template and non-template classes.
Template member functions follow all the usual rules of template functions – they can be overloaded (both by template versions and non-template versions) and they may be overridden by derived classes.
If you’d like to know more about templates, and C++ programming – particularly for embedded and real-time applications – visit the Feabhas website. You may find the following of interest:
C++-501 – C++ for Embedded Developers
C++-502 – C++ for Real-Time Developers
AC++-401 – Advanced C++ for C++11
- Practice makes perfect, part 3 – Idiomatic kata - February 27, 2020
- Practice makes perfect, part 2– foundation kata - February 13, 2020
- Practice makes perfect, part 1 – Code kata - January 30, 2020
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.
He specialises in C++, UML, software modelling, Systems Engineering and process development.
I have become a fan of yours.