MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Controlling instantiation

At times it is useful to explicitly instantiate a template; that is, to tell the compiler to lay down the code for a specific version of that template even though you’re not creating an object at that point. To do this, you reuse the template keyword as follows:

template class Bobbin<thread>;
template void sort<char>(char*[]); 
Here’s a version of the Sorted.cpp example that explicitly instantiates a template before using it:

//: C16:Generate.cpp
// Explicit instantiation
#include <iostream>
#include "Sorted.h"
#include "Integer.h"
using namespace std;

// Explicit instantiation:
template class Sorted<Integer>;

int main() {
  Sorted<Integer> is;
  Urand<47> rand1;
  for(int k = 0; k < 15; k++)
    is.add(new Integer(rand1()));
  for(int l = 0; l < is.count(); l++)
    cout << *is[l] << endl;
} ///:~ 

In this example, the explicit instantiation doesn’t really accomplish anything; the program would work the same without it. Explicit instantiation is only for special cases where extra control is needed.

Template specialization

The Sorted vector only works with objects of user-defined types. It won’t instantiate properly to sort an array of char*, for example. To create a special version you write the instantiation yourself as if the compiler had gone through and substituted your type(s) for the template argument(s). But you put your own code in the function bodies of the specialization. Here’s an example that shows a char* for the Sorted vector:

//: C16:Special.cpp
// Note -- this core dumps at least two compilers
// Template specialization
// A special sort for char*
#include <iostream>
#include "Sorted.h"
using namespace std;

// Specialize the class template:
template<> // Essential for specialization
class Sorted<char> :  public TStash<char> {
  void bubblesort();
public:
  int add(char* element) {
    TStash<char>::add(element);
    bubblesort();
    return 0; // Sort moves the element
  }
};

void Sorted<char>::bubblesort() {
  for(int i = count(); i > 0; i--)
    for(int j = 1; j < i; j++)
      if(strcmp(storage[j], storage[j-1]) < 0) {
        // Swap the two elements:
        char* t = storage[j-1];
        storage[j-1] = storage[j];
        storage[j] = t;
      }
}

char* words[] = {
  "is", "running", "big", "dog", "a",
};
const int wsz = sizeof words/sizeof *words;

int main() {
  Sorted<char> sc;
  for(int k = 0; k < wsz; k++)
    sc.add(words[k]);
  for(int l = 0; l < sc.count(); l++)
    cout << sc[l] << endl;
} ///:~ 

In the bubblesort( ) you can see that strcmp( ) is used instead of >.

Explicit specification of template functions

Taking the address of a generated template function

I am indebted to Nathan Myers for this example.

//: C19:TemplateFunctionAddress.cpp {O}
// Taking the address of a function generated
// from a template.

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

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

template <class T> 
  void i(void (*pf)(T*)) {}

int main() {
  // Full type exposition:
  h(&f<int>);
  // Type induction:
  h(&f);
  // Full type exposition:
  i<int>(&f<int>);
  // Type inductions:
  i(&f<int>);
  i<int>(&f);
} ///:~ 

Type induction in template functions

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> 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:

//: C19:ArraySize.cpp
// The return value of the template function
// asz() is a compile-time constant
#include <iostream>
#include "../arraySize.h"
using namespace std;

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.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru