Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Thinking In C++, 2nd Edition, Volume 2 Standard Libraries& Advanced Topics - Eckel B..pdf
Скачиваний:
314
Добавлен:
24.05.2014
Размер:
2.09 Mб
Скачать

getmem(p,

25);

 

for(int k

= 0;

k < 25; k++)

cout <<

p[k]

<< ' ';

freemem(p);

 

cout << '\n';

 

float* f = 0;

 

getmem(f,

3);

 

for(int u

= 0;

u < 3; u++) {

cout <<

f[u]

<< ' ';

f[u] = u + 3.14159;

}

 

 

cout << '\n';

 

getmem(f,

6);

 

for(int v

= 0;

v < 6; v++)

cout <<

f[v]

<< ' ';

freemem(f); } ///:~

After each getmem( ), the values in memory are printed out to show that the new ones have been zeroed.

Notice that a different version of getmem( ) is instantiated for the int and float pointers. You might think that because all the manipulations are so low-level you could get away with a single non-template function and pass a void*& as oldmem. This doesn’t work because then the compiler must do a conversion from your type to a void*. To take the reference, it makes a temporary. This produces an error because then you’re modifying the temporary pointer, not the pointer you want to change. So the function template is necessary to produce the exact type for the argument.

Type induction in function templates

As a simple but very useful example, consider the following:

//: :arraySize.h

//Uses template type induction to

//discover the size of an array #ifndef ARRAYSIZE_H

#define ARRAYSIZE_H

template<typename T, int size>

Chapter 15: Multiple Inheritance

129

int asz(T (&)[size]) { return size; }

#endif // ARRAYSIZE_H ///:~

This actually figures out the size of an array as a compile-time constant value, without using any sizeof( ) operations! Thus you can have a much more succinct way to calculate the size of an array at compile time:

//: C03:ArraySize.cpp

//The return value of the template function

//asz() is a compile-time constant

#include "../arraySize.h"

int main() {

int a[12], b[20];

const int sz1 = asz(a); const int sz2 = asz(b); int c[sz1], d[sz2];

} ///:~

Of course, just making a variable of a built-in type a const does not guarantee it’s actually a compile-time constant, but if it’s used to define the size of an array (as it is in the last line of main( )), then it must be a compile-time constant.

Taking the address of a generated function template

There are a number of situations where you need to take the address of a function. For example, you may have a function that takes an argument of a pointer to another function. Of course it’s possible that this other function might be generated from a template function so you need some way to take that kind of address12:

//: C03:TemplateFunctionAddress.cpp

//Taking the address of a function generated

//from a template.

template <typename T> void f(T*) {}

void h(void (*pf)(int*)) {}

12 I am indebted to Nathan Myers for this example.

Chapter 15: Multiple Inheritance

130

template <class T>

void g(void (*pf)(T*)) {}

int main() {

//Full type exposition: h(&f<int>);

//Type induction: h(&f);

//Full type exposition: g<int>(&f<int>);

//Type inductions: g(&f<int>); g<int>(&f);

}///:~

This example demonstrates a number of different issues. First, even though you’re using templates, the signatures must match – the function h( ) takes a pointer to a function that takes an int* and returns void, and that’s what the template f produces. Second, the function that wants the function pointer as an argument can itself be a template, as in the case of the template g.

In main( ) you can see that type induction works here, too. The first call to h( ) explicitly gives the template argument for f, but since h( ) says that it will only take the address of a function that takes an int*, that part can be induced by the compiler. With g( ) the situation is even more interesting because there are two templates involved. The compiler cannot induce the type with nothing to go on, but if either f or g is given int, then the rest can be induced.

Local classes in templates

Applying a function to an STL sequence

Suppose you want to take an STL sequence container (which you’ll learn more about in subsequent chapters; for now we can just use the familiar vector) and apply a function to all the objects it contains. Because a vector can contain any type of object, you need a function that works with any type of vector and any type of object it contains:

//: C03:applySequence.h

// Apply a function to an STL sequence container

Chapter 15: Multiple Inheritance

131

//0 arguments, any type of return value: template<class Seq, class T, class R> void apply(Seq& sq, R (T::*f)()) {

typename Seq::iterator it = sq.begin(); while(it != sq.end()) {

((*it)->*f)(); it++;

}

}

//1 argument, any type of return value: template<class Seq, class T, class R, class A> void apply(Seq& sq, R(T::*f)(A), A a) {

typename Seq::iterator it = sq.begin(); while(it != sq.end()) {

((*it)->*f)(a); it++;

}

}

//2 arguments, any type of return value: template<class Seq, class T, class R,

class A1, class A2>

void apply(Seq& sq, R(T::*f)(A1, A2), A1 a1, A2 a2) {

typename Seq::iterator it = sq.begin(); while(it != sq.end()) {

((*it)->*f)(a1, a2); it++;

}

}

//Etc., to handle maximum likely arguments ///:~

The apply( ) function template takes a reference to the container class and a pointer-to- member for a member function of the objects contained in the class. It uses an iterator to move through the Stack and apply the function to every object. If you’ve (understandably) forgotten the pointer-to-member syntax, you can refresh your memory at the end of Chapter XX.

Notice that there are no STL header files (or any header files, for that matter) included in applySequence.h, so it is actually not limited to use with an STL sequence. However, it does make assumptions (primarily, the name and behavior of the iterator) that apply to STL sequences.

Chapter 15: Multiple Inheritance

132

You can see there is more than one version of apply( ), so it’s possible to overload function templates. Although they all take any type of return value (which is ignored, but the type information is required to match the pointer-to-member), each version takes a different number of arguments, and because it’s a template, those arguments can be of any type. The only limitation here is that there’s no “super template” to create templates for you; thus you must decide how many arguments will ever be required.

To test the various overloaded versions of apply( ), the class Gromit13 is created containing functions with different numbers of arguments:

//: C03:Gromit.h

//The techno-dog. Has member functions

//with various numbers of arguments. #include <iostream>

class Gromit { int arf;

public:

Gromit(int arf = 1) : arf(arf + 1) {} void speak(int) {

for(int i = 0; i < arf; i++) std::cout << "arf! ";

std::cout << std::endl;

}

char eat(float) {

std::cout << "chomp!" << std::endl; return 'z';

}

int sleep(char, double) {

std::cout << "zzz..." << std::endl; return 0;

}

void sit(void) {} }; ///:~

Now the apply( ) template functions can be combined with a vector<Gromit*> to make a container that will call member functions of the contained objects, like this:

//: C03:applyGromit.cpp // Test applySequence.h #include "Gromit.h"

#include "applySequence.h"

13 A reference to the British animated short The Wrong Trousers by Nick Park.

Chapter 15: Multiple Inheritance

133

Соседние файлы в предмете Программирование