C++ has a mechanism called templates to reduce code duplication when supporting numerous data types.
A C++ function or C++ class with functions which operates on integers, float and double data types can be unified with a single template function or class with functions which is flexible enough to use all three data types.
This mechanism in C++ is called the "Template".
C++ templates fall under the category of "meta-programming" and auto code generation although one never sees the code generated.
C++ Template Functions:
In the following example we have a single template represent the code to square a number with the data types int, float and double.
Overloaded functions specified for each data type
A single template to support all data types
#include <iostream>
using namespace std;
int square (int x)
{
return x * x;
};
float square (float x)
{
return x * x;
};
double square (double x)
{
return x * x;
};
main()
{
int i, ii;
float x, xx;
double y, yy;
i = 2;
x = 2.2;
y = 2.2;
ii = square(i);
cout << i << ": " << ii << endl;
xx = square(x);
cout << x << ": " << xx << endl;
yy = square(y);
cout << y << ": " << yy << endl;
}
#include <iostream>
using namespace std;
template <class T>
inline T square(T x)
{
T result;
result = x * x;
return result;
};
main()
{
int i, ii;
float x, xx;
double y, yy;
i = 2;
x = 2.2;
y = 2.2;
ii = square<int>(i);
cout << i << ": " << ii << endl;
xx = square<float>(x);
cout << x << ": " << xx << endl;
// Explicit use of template
yy = square<double>(y);
cout << y << ": " << yy << endl;
// Implicit use of template
yy = square(y);
cout << y << ": " << yy << endl;
}
Compile: g++ test1.cpp
Run: ./a.out
2: 4
2.2: 4.84
2.2: 4.84
Compile: g++ test2.cpp
Run: ./a.out
2: 4
2.2: 4.84
2.2: 4.84
2.2: 4.84
Note:
The code used in the overloaded C++ functions example above, is repeated for each data type. The templated function is specified once.
The templated type keyword specifier can be either "class" or "typename":
template<class T>
template<typename T>
Both are valid and behave exactly the same. I prefer "typename".
The templated function works using either the explicit or implicit template expression square<int>(value) or square(value).
In the template definition, "T" represents the data type. The compiler will generate the type specific functions required. This results in a more compact code base which is easier to maintain.
The code and logic of the functions only has to be specified once in the templated function and the parameter "T" is used to represent the argument type.
The template declaration and definition must reside in the same file, typically an include header file.
A "C" macro function could also perform this purpose: #define square(x) (x * x)
The advantage of the template is that it performs type checking while the macro does not.
Template Specialization:
The following is an extended example of the square function using template specialization to support type string which requires special handling.
Template specialization to support additional data types
#include <iostream>
using namespace std;
template <class T>
inline T square(T x)
{
T result;
result = x * x;
return result;
};
// template specialization
template <>
string square<string>(string ss)
{
return (ss+ss);
};
main()
{
int i = 2, ii;
string ww("Aaa");
ii = square<int>(i);
cout << i << ": " << ii << endl;
cout << square<string>(ww) << endl;
}
Compile: g++ test.cpp
Run: ./a.out
2: 4
AaaAaa
Note that a specialized template was created to handle the string class.
Template specialization is used when a different and specific implementation is to be used for a specific data type.
Multiple Templated Types:
The following is an example of a template supporting multiple types:
#include <iostream>
using namespace std;
template <typename T, typename U>
void squareAndPrint(T x, U y)
{
T result;
U otherVar;
cout << "X: " << x << " " << x * x << endl;
cout << "Y: " << y << " " << y * y << endl;
};
main()
{
int ii = 2;
float jj = 2.1;
squareAndPrint<int,float>(ii, jj);
}
Compile: g++ test.cpp
Run: ./a.out
X: 2 4
Y: 2.1 4.41
A single type can only be specified once.
Non-type parameters:
Non-type template parameters provide the ability to pass a constant expression at compile time.
The constant expression may also be an address of a function, object or static class member.
The following is an example of a template function supporting a non-type parameter "count" used for the array size and loop count:
#include <iostream>
using namespace std;
template <typename T, int count>
void loopIt(T x)
{
T val[count];
for(int ii=0; ii<count; ii++)
{
val[ii] = x++;
cout << val[ii] << endl;
}
};
main()
{
float xx = 2.1;
loopIt<float,3>(xx);
}
Compile: g++ test.cpp
Run: ./a.out
2.1
3.1
4.1
Specify a default type parameter and default non-type parameter:
#include <iostream>
using namespace std;
template <typename T=float, int count=3>
T multIt(T x)
{
for(int ii=0; ii<count; ii++)
{
x = x * x;
}
return x;
};
main()
{
float xx = 2.1;
cout << xx << ": " << multIt<>(xx) << endl;;
}
Compile: g++ test.cpp -std=c++0x
Run: ./a.out
2.1: 378.228
Note that multIt<> with no type specified, deferred to the default type: float.
[Potential Pitfall]:
You must specify the compiler argument -std=c++0x to avoid the following error:
test.cpp:5:13: error: default template arguments may not be used in function templates without -std=c++0x or -std=gnu++0x
C++ Template Classes:
The concept of template functions can be extended to template classes.
A template class takes the form: template <class T> class MyTemplateClass { ... };
Class template specialization takes the form: template <> class MyTemplateClass <specific-data-type> { ... };
An example of a C++ template class applied to a 2 by 2 matrix:
Note that each version or "specialization" (data type) of the class will have its own unique copy of the static member.
All objects of that specialization will share the same static member.
Static member variables are initialized for each specialization/type.
Template template parameters:
#include <iostream>
using namespace std;
template <template <typename T> typename U>
class Xyz
{
....
};
Template classes and Inheritance:
A templated class may be derived from a regular non-templated C++ class or derived from another templated class:
1) Example of a templated class derived from a regular non-templated C++ class:
File: Color.hpp (non-templated base class)
#ifndef COLOR_HPP__
#define COLOR_HPP__
#include <string>
enum eColor { none = 0, red, white, blue, yellow, green, black };
class Color
{
public:
Color(eColor color);
void setColor(eColor color);
eColor getColor() { return mColor; };
std::string getStrColor();
protected:
eColor mColor;
};
Color::Color(eColor _color)
{
mColor = _color;
}
void Color::setColor(eColor _color)
{
mColor = _color;
}
std::string Color::getStrColor()
{
switch(mColor)
{
case red:
return "red";
case white:
return "white";
case blue:
return "blue";
case yellow:
return "yellow";
case green:
return "green";
case black:
return "black";
case none:
default:
return "none";
}
}
#endif
File: Circle.hpp (templated base class)
#ifndef CIRCLE_HPP__
#define CIRCLE_HPP__
#include <math.h>
#include <string>
#include "Color.hpp"
template <typename T>
class Circle : public Color
{
public:
Circle(T centerX, T centerY, T radius, eColor color);
Circle(T centerX, T centerY, T radius);
Circle(T radius);
T area();
T circumference();
T getX();
T getY();
T getRadius();
protected:
T x;
T y;
T radius;
};
template <typename T>
Circle<T>::Circle(T _x, T _y, T _radius, eColor _color)
: Color(_color)
{
x = _x;
y = _y;
radius = _radius;
}
template <typename T>
Circle<T>::Circle(T _x, T _y, T _radius)
: Color(none)
{
x = _x;
y = _y;
radius = _radius;
}
template <typename T>
Circle<T>::Circle(T _radius)
: Color(none)
{
x = const_cast<T>(0);
y = const_cast<T>(0);
radius = _radius;
}
template <typename T>
T Circle<T>::area()
{
return M_PI * radius * radius;
}
template <typename T>
T Circle<T>::circumference()
{
return const_cast<T>(2) * M_PI * radius;
}
#endif
C++ How to Program
by Harvey M. Deitel, Paul J. Deitel
ISBN #0131857576, Prentice Hall
Fifth edition. The first edition of this book (and Professor Sheely at UTA) taught me to program C++. It is complete and covers all the nuances of the C++ language.
It also has good code examples. Good for both learning and reference.
Exceptional C++: 47 Engineering Puzzles, Programming Problems and Solutions
by Herb Sutter
ISBN #0201615622, Addison-Wesley Professional
Advanced C++ features and STL.
More Exceptional C++
by Herb Sutter
ISBN #020170434X, Addison-Wesley Professional
Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition)
by Scott Meyers
ISBN #0201924889, Addison-Wesley Professional
More Effective C++: 35 New Ways to improve your Programs and Designs
by Scott Meyers
ISBN #020163371X, Addison-Wesley Professional