Templates
Your quote here -- B. Stroustrup
Independent concepts should be independently represented and
should be combined only when needed. Otherwise unrelated concepts
are bundled together or unnecessarry dependencies are created.
Templates provide a simple way to represent a wide range of general
concepts and simple ways to combine them.
A standard library requires a greater degree of generality, flexibility
and efficiency.
Overloading
int max( int a, int b)
{
if ( a > b )
return a;
else
return b;
}
double max( double a, double b)
{
if ( a > b )
return a;
else
return b;
}
// ...
Prepocessor macro:
#define MAX(a,b) a > b ? a : b
Works, because C/C++ macro is - typeless.
But a macro is processed not by the C++ compiler, therefore there are
a number of "secondary effect":
MAX( x, y)*2 --> x > y ? x : y*2
#define MAX(a,b) ((a) > (b) ? (a) : (b))
MAX( ++x, y) --> ++x > y ? ++x : y
void swap( int& x, int& y)
{
int temp = x; // !! how to detect type of x?
x = y;
y = temp;
}
Does not works with macro, because a macro is - typeless.
We need a facility to use type parameters, template depends only on
the properties that is actually uses from its parameter types and does
not require different types used as arguments to be explicitly related.
In particular, the argument types used as a template need not be from
a single inheritance hierarchy.
template <typename T>
void swap( T& x, T& y)
{
T temp = x;
x = y;
y = temp;
}
template <class T>
T max( T a, T b)
{
if ( a > b )
return a;
else
return b;
}
A template is not a single function! It is rather a schema. The process
of generating a concrete function or class declaration from a template
and a template argument is often called template instantiation. A version
of a template for a particular argument is called a specialization.
A template parameter can be
-- constant expression
-- address of an object or function with external linkage in form of &obj, or f
-- non-overloaded pointer to member
But can not be a string literal: "hello"
Instantiation, parameter deduction
The instantiation is - in most cases - an automatic process.
int i = 3, j = 4, k;
double x = 3.14, y = 4.14, z;
const int ci = 6;
k = max( i, j); // -> max(int, int)
z = max( x, y); // -> max(double, double)
k = max( i, ci); // -> max(int, int), with trivial conversion
z = max( i, x); // -> ambiguous, no standard conversion
template <class T, class S>
T max( T a, S b)
{
if ( a > b )
return a;
else
return b;
}
int i = 3;
double x = 3.14;
z = max( i, x); // ok, but..
z == 3.0 // ??
There is no way to deduce type parameter in runtime.
template <class R, class T, class S>
R max( T a, S b)
{
if ( a > b )
return a;
else
return b;
}
z = max( i, x); // error
Of course, the return type has no role in deduction.
Template argument R is not deducable.
template <class R, class T, class S>
R max( T a, S b, R)
{
if ( a > b )
return a;
else
return b;
}
z = max( i, x, 0.0); // ok, but ugly and misleading
Explicit specialization
template <class R, class T, class S>
R max( T a, S b)
{
if ( a > b )
return a;
else
return b;
}
z = max<double>( i, x); // ok, returns 3.14
k = max<long, long, int>( i, x); // converts to long and int
k = max<int, int, int>( i, j); // unnecessary
Template overloading
template <class T> T max(T,T);
template <class R, class T, class S> R max(T,S);
User specializations
char *s1 = "Hello";
char *s2 = "world";
cout << max( s1, s2); // ??
template <>
const char *max<const char *>( const char *s1, const char *s2)
{
return strcmp( s1, s2) < 0 ? s1 : s2;
}
or in shorter form:
template <> const char *max( const char *s1, const char *s2)
Template classes
Since class template parameters regularly cannot be deduced from
constructor parameters, objects from template classes must be
explicitly specialized:
#include <iostream>
#include "matrix.h"
using namespace std;
int main()
{
matrix<int> m(10,20);
m.at(2,3) = 1;
cout << m(2,3) << endl;
return 0;
}
Consider all the member functions are template functions for a
template class. Even those are templates, where there is no explicite
use of parameter T.
// even this is a template:
int rows() const { return x; }
Template class specialization
Let familiar with this ugly notation: namespace is matrix<T>, type of
class is matrix<T>, inside the namespace matrix is matrix<T> by default
matrix<T>& matrix<T>::operator+=( const matrix &other) ...
The reason is the template class specialization:
template <>
class matrix<bool>
{
//
// totally different code
//
}
matrix<bool>& matrix<bool>::operator+=( const matrix &other) ...
Partial specialization
There is possible to partially specialize a class template
template <class A, class B>
class C
{
// ...
}
template <class B>
class C<concreateType,B>
{
// ...
}
Default Parameter
You can define default template arguments. They may even refer to
previous template parameters:
template <class T, class C = deque<T> >
class std::stack
{
// ...
};