Templates Indepth
- Non type template parameters
- Default template arguments
- Template template arguments
- The "typename" keyword
- Member Function Templates
- Function Templates Caveats
In the code sample below N is a non type template parameter.
template<class T, size_t N> class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// Etc.
};
* The non-type template parameter MUST be an integral value that is known at compile
time
In the following code sample the second template parameter has a default argument.
template<class T, size_t N = 100> class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// Etc.
};
* You can provide default arguments for template parameters in class templates, but NOT in function templates. (why?)
* They should only be defined once, the first time a template declaration or definition is seen by the compiler.
* Once you introduce a default argument, all the subsequent template parameters must also have defaults.
* You can choose to provide defaults for all arguments, but you must use an empty set of brackets when declaring an instance so that the compiler knows that a class template is involved.
template<class T = int, size_t N = 100> // Both defaulted
class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// Etc.
};
* If default arguments happen to be templatized as below, put the space between the last two right angle bracket characters as it prevents the compiler from interpreting those two characters (>>) as the right-shift operator.
template<class T, class Allocator = allocator<T> >
class vector;
Following code sample is an example of a class template with a template parameter as the argument.
template<class T, template<class> class Seq>
class Container {
Seq<T> seq;
public:
void append(const T& t) { seq.push_back(t); }
T* begin() { return seq.begin(); }
T* end() { return seq.end(); }
};
* The class Seq is another class template. For example an Array class template could be a trivial sequence container.
* When the compiler looks at the inner parameters of a template template parameter, default arguments are not consideed so you MUST repeat the defaults in order to get a exact match.
The default dimension of 10 is required in the line:
template<class T, template<class, size_t = 10> class Seq>
* The above situation is the ONLY exception to the rule that default arguments must not be repeated in a compilation unit.
* If a type referred to inside template code is qualified by a template type parameter, you must use the typename keyword as a prefix, unless it appears in a base class specification or initializer list in the same scope (in which case you MUST not).
* Without "typename", the compiler would assume that the expression Seq
#include <iostream>
#include <list>
#include <memory>
#include <vector>
using namespace std;
template<class T, template<class U, class = allocator<U> >
class Seq>
void printSeq(Seq<T>& seq) {
for(typename Seq<T>::iterator b = seq.begin();
b != seq.end();)
cout << *b++ << endl;
}
* "typename can be used as an option to the keyword "class" within the template argument list of a template definition.
The complex template in C++ Standard Library has a type parameter meant to represent an underlying floating-point type to hold the real and imaginary parts of a complex number. The following code snippet from the standard library shows a member-template constructor in the complex class template:
template<typename T> class complex {
public:
template<class X> complex(const complex<X>&);
...
...
};
usage
complex<float> z(1, 2);
complex<double> w(z);
Here the member templates enables flexible conversion easy. The complex template parameter T is double whereas X is a float.
* Defining a template within a template is a nesting operation, so the prefixes that introduce the templates must reflect this nesting if you define the member template outside the outer class definition
template<typename T>
template<typename X>
complex<T>::complex(const complex<X>& c) {/* Body here… */}
With this a vector of ints can be used to initialize a vector of doubles. which is legitimate conversion.
* Member templates can also be classes. (They don’t need to be functions.)
* Member template functions cannot be declared virtual. Current compiler technology expects to be able to determine the size of a class’s virtual function table when the class is parsed.
*While instantiating function templates you can often omit the template arguments. One MUST always use angle brackets when instantiating class.
* Function templates can deduce the type to be used in templates from the types of the function arguments.
* Default template arguments are not even allowed with function templates as opposed to class templates. (why?)
* If template arguments are avoided when instantiating function templates NO standard conversion are performed. For eg. Consider the following function template
template<typename T, typename U>
const T& min(const T& a, const U& b) {
return (a < b) ? a : b;
}
Will fail (As compiler does not know which type to deduce. Double or int?):
int z = min(x, j); // x is a double ; j is int
Will pass:
int z = min<double>(x, j);
Will pass:
int z = min(x, (double)j);
* If the RETURN type is an INDEPENDANT template parameter the function MUST be called with explicit type specification. Consider the following example where R is a template parameter return type.
template<typename R, typename P>
R implicit_cast(const P& p) {
return p;
}
int main() {
int i = 1;
float x = implicit_cast<float>(i);
int j = implicit_cast<int>(x);
//! char* p = implicit_cast<char*>(i); // Fails because there is no conversion from int to char*
}
If you interchange R and P in the template parameter list near the top of the file, it will be impossible to compile this program because the return type will remain unspecified
0 Comments:
Post a Comment
<< Home