Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
The bitset::to_string( ) function template is an
example of a member template: a template declared within another class
or class template. This allows many combinations of independent template
arguments to be combined. A useful example is found in the complex class
template in the Standard C++ library. The complex template 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>&);
The standard complex template comes ready-made with
specializations that use float, double, and long double
for the parameter T. The member-template constructor above creates a new
complex number that uses a different floating-point type as its base type, as
seen in the code below:
complex<float> z(1, 2);
complex<double> w(z);
In the declaration of w, the complex template
parameter T is double and X is float. Member
templates make this kind of flexible conversion easy.
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.
For example, if you implement the complex class template, and if you
define the member-template constructor outside the complex template
class definition, you do it like this:
template<typename T>
template<typename X>
complex<T>::complex(const
complex<X>& c) {/* Body here */}
Another use of member function templates in the standard
library is in the initialization of containers. Suppose we have a vector
of ints and we want to initialize a new vector of doubles
with it, like this:
int data[5] = { 1, 2, 3, 4, 5 };
vector<int> v1(data, data+5);
vector<double> v2(v1.begin(), v1.end());
As long as the elements in v1 are
assignment-compatible with the elements in v2 (as double and int
are here), all is well. The vector class template has the following
member template constructor:
template<class InputIterator>
vector(InputIterator first, InputIterator last,
const Allocator& = Allocator());
This constructor is used twice in the vector
declarations above. When v1 is initialized from the array of ints,
the type InputIterator is int*. When v2 is initialized
from v1, an instance of the member template constructor is used with InputIterator
representing vector<int>::iterator.
Member templates can also be classes. (They don t need to be
functions.) The following example shows a member class template inside an outer
class template:
//: C05:MemberClass.cpp
// A member class template.
#include <iostream>
#include <typeinfo>
using namespace std;
template<class T> class Outer {
public:
template<class R> class Inner {
public:
void f();
};
};
template<class T> template<class R>
void Outer<T>::Inner<R>::f() {
cout << "Outer == " <<
typeid(T).name() << endl;
cout << "Inner == " <<
typeid(R).name() << endl;
cout << "Full Inner == " <<
typeid(*this).name() << endl;
}
int main() {
Outer<int>::Inner<bool> inner;
inner.f();
} ///:~
The typeid operator, covered in Chapter 8, takes a
single argument and returns a type_info object whose name( )
function yields a string representing the argument type. For example, typeid(int).name( )
might return the string int. (The actual string is
platform-dependent.) The typeid operator can also take an expression and
return a type_info object representing the type of that expression. For
example, with int i, typeid(i).name( ) returns something
like int, and typeid(&i).name( ) returns something
like int *.
The output of the program above should be something like
this:
Outer == int
Inner == bool
Full Inner == Outer<int>::Inner<bool>
The declaration of the variable inner in the main
program instantiates both Inner<bool> and Outer<int>.
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. Allowing virtual
member template functions would require knowing all calls to such member
functions everywhere in the program ahead of time. This is not feasible,
especially for multi-file projects.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |