Templates

Document Sample
scope of work template
							Templates

An introduction
    Simple Template Functions
template <typename T>
T max(T x, T y) {
   if (x > y) { return x; }
   else { return y; }
}
int main(void) {
  int x = 0;
  cout << max(x, ++x); // prints 1
  cout << x; // prints 1
  string s = “hello”;
  string t = “world”;
  cout << max(s, t); // prints “world”
}
           What’s all this?
• Templates have ugly syntax. Get used
  to it, and on an exam, be able to “get in
  the ballpark” of the correct syntax.
• Conceptually, templates are a lot like
  macros, just without the unpleasant side
  effects.
  – When you see a template, or use a
    template, think “text substitution” and you’ll
    be close
        Template Instantiation
• A template definition is a recipe that the compiler can
  use to generate a function (or a class, more on that
  later)
• The compiler will not use this recipe unless/until you
  instantiate the template.
   – At that point, the compiler goes and performs the text
     substitution you asked for, and then compiles the newly
     generated function as if you’d written that function yourself.
• What happens if I instantiate the same template
  multiple different ways?
   – Well, with function overloading, we just get two or more
     functions with the same name, but with different arguments!
 Example (template definition)
template <typename T, typename U>
T max(T x, T y) {
  if (x < y) { return y; }
  else { return x; }
}
• “T” is the template parameter. Since the “T” is specified as a
     “typename” (i.e., the name of some type), then “T” can be replaced by
     any type (e.g., “int” or “string”). T can NOT be replaced by any arbitrary
     text, just by a type.
• We have “defined” this template, which means the compiler now knows
     the recipe. But there is no machine code for the max function yet. The
     compiler won’t actually compile the max function until we instantiate it.
    – The compiler does do some preliminary syntax checking, so you can get
      compiler errors in your template definitions even if you don’t instantiate
      them.
         Example (instantiations)
int main(void) {
   int x = 3;
   x = max(x, 5); // instantiation #1
   double y = max(1.0, 5.0); // instantitation #2
   y = max(y, y + 1); // not a new instantiation
   y = max(x, y); // uh oh! Ambiguous
   y = max<double>(x, y); // OK, the choice for T is explicitly “double”
}
• For the first instantiation, the compiler can easily guess that “T” should be “int”
• For the second instantiation, it is also obvious that “T” should be “double”
     (floating point constants are double)
• The compiler instantiates a different version of the template for each distinct
     binding of T that you use (i.e., one time for int one time for double), not each
     time you call the function.
• Since both arguments to max are supposed to be the same type (T), the
     compiler can’t figure out what to do with the ambiguous line, should T be int
     (convert y) or should T be double (convert x).
• We can remove the ambiguity in a few ways, the most certain is to simply tell
     the compiler what argument you want for the “type paremeter” T.
               Template Classes
• C++ also provides template classes.
• Virtually any “data structure” (AKA “collection”, AKA “container”)
  will be implemented in C++ as a template
    – The type of data stored in the structure is really not at all relevant to
      the data structure itself.
• Template classes get “defined” and “instantiated” in analogous
  ways to template functions with the following caveats
    – The compiler will never guess at the template argument for a
      template class, you must always explicitly tell the compiler “what T
      is”.
    – Classes cannot be “overloaded”, but the compiler will permit you to
      instantiate the same template class in multiple ways.
        • Each distinct instantiation results in a completely distinct class! (with its
          own copy of the static data members, for example).
    – The member functions in a template class are template functions
      (oh, how confusing!)
                                     An example
template <typename T>
class Foo {
  T x;
  static int count;
public:
  Foo() {
    x = 0;
    count += 1;
  }
  T getX(void) { return x; }
   int howMany(void) { return count; }
};
template <typename T>
int Foo<T>::count = 0;
int main(void) {
    Foo<int> a;
    cout << a.getX() << endl; // prints 0
    Foo<int> b;
    cout << b.count() << endl; // prints 2
    Foo<double> c;
    cout << c.count() << endl; // prints 1
}
 A Useful Example (abridged)
template <typename T>
class vector {
public:
   void push_back(T);
   void pop_back(void);
   T& operator[](int k);
   explicit vector(int initial_capacity=8);
private:
   T* data;
   int length;
   int capacity;
   void expand(void); // increase capacity
};
    Member functions are template
          functions, ugh.
template <typename T>
vector<T>::vector(int init_cap) {
  capacity = init_cap;
  data = new T[capacity];
  length = 0;
}
template <typename T>
void vector<T>::push_back(T x) {
  if (capacity == length) { expand(); }
  data[length] = x;
  length += 1;
}
template <typename T>
void vector<T>::expand(void) {
  capacity *= 2;
  T* new_data = new T[capacity];
  for (int k = 0; k < length; k += 1) { new_data[k] = data[k]; }
  delete[] data;
  data = new_data;
}
             Templates and .h files
•   C++ programs (just like C) are intended to be “separately compiled”.
     –   Each module can be compiled independently and then linked together at the end to
         form the executable program.
     –   This is nice for large development teams.
•   Type definitions and function declarations that are “public” (i.e., used by more
    than one module in the system) are usually placed into a .h file
     –   Any module that needs to know about these functions or types simply #includes the .h
         file.
     –   When that module is compiled, the compiler checks the syntax by which you are calling
         those functions, but the compiler doesn’t actually generate machine code for those
         functions – i.e., the compiler uses the .h file just to make sure the modules will
         “interoperate”.
•   BUT templates require that the compiler instantiate them (or else there’s no
    machine code to interoperate with). And the compiler won’t know which
    templates to instantiate (or how to instantiate them) until it looks at all the other
    modules in the project.
     –   In practice this means that the entire template (not just declarations) must be placed
         into the .h file.
     –   If you’re writing a template class, you might consider just accepting this, and expanding
         all your member functions in place (inside the class definition).

						
Related docs