Lecture 5. Basics Part 4
Lecture 5. Basics Part 4
ENEL3CCH1
Week Five
Recap of Week 4
Explicit and Implicit specialisation
Member function templates
Class templates
Discipline of Electrical, Electronic & Computer Engineering Bashan Naidoo
What we cover in week 5
&x xptr 20 x
Aside: Pointers and references
Let us consider references…
&x >
40 x xref
Function overloading and templates
• This template
#include <iostream> replaces all the
#include <string> overloaded
definitions of
NTswap().
/******************Function templates******************
* Since the structure and logic are identical in all *
* above functions, we can replace them with a single * • This must precede
* function template... below * main()
******************************************************/
• It can even process
template <typename T> strings
void NTswap(T &Num1, T &Num2) {
T temp = Num1;
Num1 = Num2; Note: These are
Num2 = temp; ordinary functions (not
} methods), and this is a
POP example (not OOP)
Function overloading and templates
From the compiler’s perspective…
• A template is not a normal function or class.
• It cannot be directly compiled. Why?
Generic single
template template
Specific
Function template template template
(specialisaions) specialisation specialisation specialisation
Function overloading and templates
Instantiation of
specialisations From the compiler’s perspective…
Specialisation
(compileable
C++ code)
Function overloading and templates
Implicit instantiation of Implicit Specialisation Example #include <iostream>
specialisations typename using namespace std;
int main () {
instantiation int i=5, j=6, k;
k =GetMax<int>(i,j); n =GetMax<long>(l,m); long l=10, m=5, n;
k =GetMax< int >(i,j);
n=GetMax< long >(l,m);
int GetMax (int a, int b) { long GetMax (long a, long b) {
cout << k << endl;
return (a>b?a:b); return (a>b?a:b);
cout << n << endl;
} }
return 0;
Specialisation }
Compileable C++ code
Function overloading and templates
Explicit instantiation of Explicit Specialisation Example
specialisations (multiple template parameters) //code frag from main()
instantiation OOPS!
i = GetMin<int,long> (j,l); Exercise for you!
The function returns an
int. Can it return b as
int GetMin (int a, long b) { an int? Will this
return (a<b?a:b); compile? You test this
} and fix it if needed.
Specialisation
Compileable C++ code
Introduction to templates in OOP
So we have already looked at…
• Function overloading
• Function templates
• Instantiation of implicit and explicit template specialisations
These were presented as simple POP concepts where a main program calls
an overloaded function or a template, that is defined outside of main().
• Class templates
int main()
{
MFTdemo1 OBJ1;
std::cout << "OBJ1.ModMyVar(3) = " << OBJ1.ModMyVar(3) << "\n";
}
Major limitation: Can only
use integer values!!!
Member function templates
Now we convert member function into a member template and test it
#include <iostream> This is a normal class Output:
with a public
class MFTdemo2 { template member OBJ2.ModMyVar(3) = 9
public: function OBJ2.ModMyVar(3.6) = 10.8
template<typename T>
T ModMyVar(T var) {
return 3 * var; Implicit specialisation of
} the member function
}; template has produced the
desired output…
int main()
{
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3) = " << OBJ2.ModMyVar(3) << "\n";
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
} Problem Sorted!
Member function templates
Now we convert member function into a member template and test it
#include <iostream>
Complexity is shifted Output:
into the class
class MFTdemo2 {
Remember… OBJ2.ModMyVar(3) = 9
public:
• ******Abstraction****** OBJ2.ModMyVar(3.6) = 10.8
template<typename T>
• Write once, use many
T ModMyVar(T var) {
return 3 * var;
} Simple, happy calling program
}; Totally blind to the hidden complexity
Remember:
int main() • One class definition, many instances!
{ • Write once (complex), use many (simple)
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3) = " << OBJ2.ModMyVar(3) << "\n";
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Member function templates
An alternate syntax for the template member function
#include <iostream>
This is a normal class Output:
class MFTdemo2 {
with a public template
public: OBJ2.ModMyVar(3.6) = 10.8
member declaration
template<typename T>
T ModMyVar(T var);
};
The template member function is just like a
template<typename T> free template function but…
T MFTdemo2::ModMyVar(T var) { it is declared inside the containing class, and
return 3 * var; its external definition is scoped to its
} containing class.
int main(){
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Member function templates
Now we combine and test template and non-template member overloads
class MFTdemo3 {
public: This is a normal class Output:
int ModMyVar(int var) { with template and non-
return 5 * var; template overloads of OBJ3.ModMyVar(3) = 15
} ModMyVar() OBJ3.ModMyVar(3.6) = 10.8
template<typename T>
T ModMyVar(T var) {
return 3 * var; Executes the non-template Executes the template
} member function if it member function. A non-
}; exists; which is the case. template overload does not
(output = 5 * 3) exist (output = 3 * 3.6)
int main() {
MFTdemo3 OBJ3;
std::cout << "OBJ3.ModMyVar(3) = " << OBJ3.ModMyVar(3) << "\n";
std::cout << "OBJ3.ModMyVar(3.6) = " << OBJ3.ModMyVar(3.6) << "\n";
}
Class templates
Class templates are similar to function templates
• Each specialisation will have the same logic and structure, but
will have unique datatypes (like the overloads it replaces).
Class templates
First we look at a normal class…
Output:
#include <iostream>
My height is 183 cm.
class Measurement {
private:
int value;
public:
int getValue() { return value; }
void setValue(int val) { value = val; }
};
int main() {
Measurement heightCM;
heightCM.setValue(183);
std::cout << "My height is " << heightCM.getValue() << " cm.\n";
}
Class templates
Convert it to a class template…
Output:
#include <iostream>
template<typename T> My height is 183 cm.
class Measurement {
private:
T value;
public: We are forced to use
T getValue() { return value; } EXPLICIT specialisation.
void setValue(T val) { value = val; } Can you explain why?
};
int main() {
Measurement<int> heightCM;
heightCM.setValue(183);
std::cout << "My height is " << heightCM.getValue() << " cm.\n";
}
Class templates
template<typename T>
class Measurement {
private:
T value;
public:
T getValue() { return value; }
void setValue(T val) { value = val; }
};
int main() {
Measurement<int> VoltV;
VoltV.setValue(230);
std::cout << "Eskom voltage should be " << VoltV.getValue() << " V (nominal).\n";
std::cout << "Eskom voltage should be at least " << VoltV.RangeMin(5) << " V.\n";
}
Output:
Yes…
Template classes with template
member functions
#include <iostream>
template<typename T>
class Measurement {
private: We have a template class
T value; …with…
public: a template member declaration
T getValue() { return value; }
void setValue(T val) { value = val; }
template<typename toll>
Here we have the
toll RangeMin(toll tollerance);
template member definition
};
that is scoped to the
containing template class
template <typename T> Measurement<T>
template <typename toll>
toll Measurement<T>::RangeMin(toll tollerance) {
return value - value * tollerance / 100;
}
Template classes with template
member functions
#include <iostream>
template<typename T>
class Measurement {
WARNING! This is not a class…
private:
It is a template that represents
T value;
several class overloads!!!
public:
T getValue() { return value; }
There are many versions of this
void setValue(T val) { value = val; }
class…
template<typename toll>
toll RangeMin(toll tollerance);
}; So… the compiler must scope
this function into the correct
class version… so we fully
template <typename T> specify the class instance like
template <typename toll> this… Measurement<T>
toll Measurement<T>::RangeMin(toll tollerance) {
return value - value * tollerance / 100;
}
Template classes with template
member functions
#include <iostream>
template<typename T>
class Measurement {
private:
T value;
public:
T getValue() { return value; }
1. class tempate void setValue(T val) { value = val; }
template<typename toll> 3. class name (full)
toll RangeMin(toll tollerance);
};
2. method tempate
int main() {
Measurement<int> VoltV;
VoltV.setValue(230);
std::cout << "Eskom voltage should be " << VoltV.getValue() << " V (nominal).\n";
std::cout << "Eskom voltage should be at least " << VoltV.RangeMin(5.0) << " V.\n";
}
Output:
int main(){
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Aside: Towards “Inclusion Model” of organising
template class code
#pragma once #include “MFTdemo.h”
int main(){
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Aside: Towards “Inclusion Model” of organising
template class code
Each cpponce
#pragma file compiles independently and apparently
#include “MFTdemo.h” successfully…
but linking seems to fail. Why?
class MFTdemo2 { template<typename T>
public: T MFTdemo2::ModMyVar(T var) {
In order to instantiate
template<typename T> the template return
(early3 * binding),
var; the compiler
T ModMyVar(T
needs var);
two things… }
};
• The template definition (we placed it in MFTdemo.cpp)
• The call to the template function (we placed it in MyProg.cpp)
But these<iostream>
#include are in two files that compile separately. So when
#include “MFTdemo.h”
compiling MyProg.cpp, the template definition is missing, and when
compiling
int main(){MFTdemo.cpp the call is missing. The binding is not
clear and the
MFTdemo2 OBJ2;compiler creates a reference for the linker to
std::cout
resolve. << "OBJ2.ModMyVar(3.6)
Unfortunately = " << OBJ2.ModMyVar(3.6)
the linker << "\n"; it.
may fail to resolve
}
Aside: Towards “Inclusion Model” of organising
#pragma once template class code
class MFTdemo2 {
public: #include “MFTdemo.h”
template<typename T>
T ModMyVar(T var); template<typename T>
}; T MFTdemo2::ModMyVar(T var) {
return 3 * var;
template<typename T> }
T MFTdemo2::ModMyVar(T var) {
return 3 * var;
Solution one: Insert all the class code in the class
}
header file and remove the class .cpp file. This
effectively produces one file again before
compilation. So early binding is successful.
#include <iostream>
#include “MFTdemo.h” Unfortunately, we no longer have separate
declaration and definition files for our class.
int main(){ Solution works but it is not comfortable.
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Aside: Towards “Inclusion Model” of organising
template class code
#pragma once #include “MFTdemo.h”
#include <iostream> Solution two: Easy fix, works well, but we are
#include “MFTdemo.h” now including .cpp files. This breaks traditional
#include “MFTdemo.cpp” practice where only .h files are included. This
leads us to solution three…
int main(){
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}
Aside: Towards “Inclusion Model” of organising
template class code
#pragma once #pragma once
#include “MFTdemo.h”
class MFTdemo2 {
public: template<typename T>
template<typename T> T MFTdemo2::ModMyVar(T var) {
T ModMyVar(T var); return 3 * var;
}; }
#include <iostream>
#include “MFTdemo.h” Solution Three: best solution for
#include “MFTdemoDefs.h” templated classes!!!
int main(){
MFTdemo2 OBJ2;
std::cout << "OBJ2.ModMyVar(3.6) = " << OBJ2.ModMyVar(3.6) << "\n";
}