functiontemplate.cc

#include <iostream>  // cout, endl
#include <string>    // string
#include <cstdlib>   // EXIT_SUCCESS

// returns 0 if equal, 1 if value1 is bigger, -1 otherwise
template <typename T>
int compare(const T& value1, const T& value2) {
  if (value1 < value2) return -1;
  if (value2 < value1) return 1;
  return 0;
}

int main(int argc, char** argv) {
  std::string h("hello"), w("world");
  std::cout << compare<int>(10, 20) << std::endl;
  std::cout << compare<std::string>(h, w) << std::endl;
  std::cout << compare<double>(50.5, 50.6) << std::endl;
  return EXIT_SUCCESS;
}

functiontemplate_infer.cc

#include <iostream>  // cout, endl
#include <string>    // string
#include <cstdlib>   // EXIT_SUCCESS

// returns 0 if equal, 1 if value1 is bigger, -1 otherwise
template <typename T>
int compare(const T& value1, const T& value2) {
  if (value1 < value2) return -1;
  if (value2 < value1) return 1;
  return 0;
}

int main(int argc, char** argv) {
  std::string h("hello"), w("world");
  std::cout << compare(10, 20) << std::endl;  // ok
  std::cout << compare(h, w) << std::endl;  // ok
  std::cout << compare("Hello", "World") << std::endl;  // hm...
  return EXIT_SUCCESS;
}

valtemplate.cc

#include <string>   // string
#include <cstdlib>  // EXIT_SUCCESS

// Return a pointer to a new N-element heap array filled with val
// (not entirely realistic, but shows what's possible)
template <typename T, int N>
T* valarray(const T& val) {
  T* a = new T[N];
  for (int i = 0; i < N; ++i)
    a[i] = val;
  return a;
}

int main(int argc, char** argv) {
  int* ip = valarray<int, 10>(17);
  std::string* sp = valarray<std::string, 17>("hello");

  delete[] ip;
  delete[] sp;
  return EXIT_SUCCESS;
}

Pair

Pair.h

#ifndef PAIR_H_
#define PAIR_H_

// class template definition has the template
// parameter ("Thing") in scope throughout
template <typename Thing> class Pair {
 public:
  Pair() = default;  // default constructor still initializes data members

  Thing get_first() const { return first_; }    // inline definition
  Thing get_second() const { return second_; }  // inline definition
  void set_first(const Thing& copyme);
  void set_second(const Thing& copyme);
  void Swap();

 private:
  Thing first_, second_;
};

// The compiler must see the definition for any template that is
// used.  That means customers of Pair.h need to be
// shown the definition of class Pair; one way to do this is to
// include the .cc file associated with the .h file right in
// the header, as follows.  This is the "inclusion compilation
// model."

#include "Pair.cc"

#endif  // PAIR_H_

Pair.cc

// Class template member functions defined outside of the
// class template definition need a template function
// declaration that matches the template parameter list.
// An instance of each template function is instantiated
// when the class template is instantiated and that member
// function is invoked somewhere in code.

// The template parameter list is usually found on a
// separate line from the template function due to the
// lengths of function parameter lists.
// There is no change in behavior, just readability.
template <typename Thing>
void Pair<Thing>::set_first(const Thing& copyme) {
  first_ = copyme;
}

template <typename Thing>
void Pair<Thing>::set_second(const Thing& copyme) {
  second_ = copyme;
}

template <typename Thing>
void Pair<Thing>::Swap() {
  Thing tmp = first_;
  first_ = second_;
  second_ = tmp;
}


// Nonmember function for ostream output -- individual
// template parameters can take any name, but usually we
// choose to be consistent.  T chosen here instead of Thing
// for brevity and for fitting onto lecture slides.
template <typename T>
std::ostream& operator<<(std::ostream& out, const Pair<T>& p) {
  return out << "Pair(" << p.get_first() << ", " << p.get_second() << ")";
}

usepair.cc

#include <iostream>  // cout, endl
#include <string>    // string
#include <cstdlib>   // EXIT_SUCCESS

#include "Pair.h"

using std::cout;
using std::endl;
using std::string;

int main(int argc, char** argv) {
  Pair<string> ps;  // this usage instantiates a class Pair<std::string>
  string x("foo"), y("bar");

  ps.set_first(x);     // first_ = "foo", second_ = ""
  ps.set_second(y);    // first_ = "foo", second_ = "bar"
  ps.Swap();           // first_ = "bar", second_ = "foo"
  cout << ps << endl;  // nonmember overloaded operator<< invoked

  return EXIT_SUCCESS;
}

Tracer

Tracer.h

#ifndef TRACER_H_
#define TRACER_H_

#include <iostream>
#include <string>

// This class is useful when you are exploring the behavior of STL
// containers.  The class gives each instance that is manufactured
// using the default constructor a unique ID and a current value.  The
// current value is initialized to be the unique ID.
//
// Whenever an object is manufactured using the copy constructor, its
// value is copied from the argument.  Similarly, whenever an object
// is the target of an assignment operator, then its value is copied
// from the source.
//
// Finally, objects print messages out whenever important events
// happen, including default construction, copy construction,
// destruction, assignment, and "less than" operator invocation.
// (Less than is used as a comparator for things like sorting or
// inserting into the right sorted spot in a map.)
class Tracer {
 public:
  // Constructors.
  Tracer();
  Tracer(unsigned int val);
  Tracer(const Tracer& rhs);
  // Destructor.
  ~Tracer();

  // Assignment operator.
  Tracer& operator=(const Tracer& rhs);

  // Less-than comparison operator.
  bool operator<(const Tracer& rhs) const;

  // << operator
  friend std::ostream& operator<<(std::ostream& out, const Tracer& rhs);

 private:
  // Return "(id_,value_)" as a string
  std::string PrintID(void) const;

  // This static (class member) tracks the next id_ to hand out.
  static unsigned int nextid_;
  unsigned int id_;
  unsigned int value_;
};

#endif  // TRACER_H_

Tracer.cc

#include <iostream>
#include <sstream>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::ostream;
using std::string;
using std::stringstream;

Tracer::Tracer() : id_(Tracer::nextid_++), value_(id_) {
  cout << "Tracer" << PrintID() << endl;
}

Tracer::Tracer(unsigned int val) : id_(Tracer::nextid_++), value_(val) {
  cout << "Tracer" << PrintID() << endl;
}

Tracer::Tracer(const Tracer& rhs) : id_(Tracer::nextid_++), value_(id_) {
  cout << "TracerCopy[" << PrintID();
  cout << "<--" << rhs.PrintID() << "]" << endl;
  value_ = rhs.value_;
}

Tracer::~Tracer() {
  cout << "~Tracer" << PrintID() << endl;
}

Tracer& Tracer::operator=(const Tracer& rhs) {
  cout << "Tracer" << PrintID() << "=" << rhs.PrintID() << endl;
  value_ = rhs.value_;
  return *this;
}

bool Tracer::operator<(const Tracer& rhs) const {
  cout << "Tracer" << PrintID() << "<" << rhs.PrintID() << endl;
  return value_ < rhs.value_;
}

string Tracer::PrintID(void) const {
  stringstream ss;
  string paren("("), comma(","), closeparen(")");
  ss << paren << id_ << comma << value_ << closeparen;
  return ss.str();
}

ostream& operator<<(ostream& out, const Tracer& rhs) {
  out << "(" << rhs.id_ << "," << rhs.value_ << ")";
  return out;
}

unsigned int Tracer::nextid_ = 0;

vectorfun.cc

#include <iostream>
#include <vector>

#include "Tracer.h"

// Conditional compilation for displaying vector capacity.
// Note: capacity() is only valid for vector and string.
#ifdef CAP
#define CAPACITY(x) cout << #x << ".capacity() = " << x.capacity() << endl
#else
#define CAPACITY(x)
#endif

using std::cout;
using std::endl;
using std::vector;

int main(int argc, char** argv) {
  Tracer a, b, c;
  vector<Tracer> vec;

  vec.reserve(4);  // Note: reserve() is only valid for vector and string
  CAPACITY(vec);
  cout << "vec.push_back " << a << endl;
  vec.push_back(a);
  CAPACITY(vec);
  cout << "vec.push_back " << b << endl;
  vec.push_back(b);
  CAPACITY(vec);
  cout << "vec.push_back " << c << endl;
  vec.push_back(c);
  CAPACITY(vec);

  cout << "vec[0]" << endl;
  cout << vec[0] << endl;

  cout << "vec[2]" << endl;
  cout << vec[2] << endl;

  return EXIT_SUCCESS;
}

vectoriterator.cc

#include <iostream>
#include <vector>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::vector;

int main(int argc, char** argv) {
  Tracer a, b, c;
  vector<Tracer> vec;

  vec.push_back(a);
  vec.push_back(b);
  vec.push_back(c);

  cout << "Iterating:" << endl;
  vector<Tracer>::iterator it;
  for (it = vec.begin(); it < vec.end(); it++) {
    cout << *it << endl;
  }
  cout << "Done iterating!" << endl;
  return EXIT_SUCCESS;
}

vectoriterator_2011.cc

#include <iostream>
#include <vector>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::vector;

int main(int argc, char** argv) {
  Tracer a, b, c;
  vector<Tracer> vec;

  vec.push_back(a);
  vec.push_back(b);
  vec.push_back(c);

  cout << "Iterating:" << endl;
  // "auto" is a C++11 feature not available on older compilers
  for (auto p : vec) {
    cout << p << endl;
  }
  cout << "Done iterating!" << endl;
  return EXIT_SUCCESS;
}

vectoralgos.cc

#include <iostream>
#include <vector>
#include <algorithm>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::vector;
using std::sort;

void PrintOut(const Tracer& p) {
  cout << "  printout: " << p << endl;
}

int main(int argc, char** argv) {
  Tracer a, b, c;
  vector<Tracer> vec;

  vec.push_back(c);
  vec.push_back(a);
  vec.push_back(b);

  cout << "sort:" << endl;
  sort(vec.begin(), vec.end());
  cout << "done sort!" << endl;
  for_each(vec.begin(), vec.end(), PrintOut);

  return EXIT_SUCCESS;
}

listexample.cc

#include <iostream>
#include <list>
#include <algorithm>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::list;

void PrintOut(const Tracer& p) {
  cout << "  printout: " << p << endl;
}

int main(int argc, char** argv) {
  Tracer a, b, c;
  list<Tracer> lst;

  lst.push_back(c);
  lst.push_back(a);
  lst.push_back(b);

  cout << "sort:" << endl;
  lst.sort();
  cout << "done sort!" << endl;
  for_each(lst.begin(), lst.end(), PrintOut);

  return EXIT_SUCCESS;
}

mapexample.cc

#include <iostream>
#include <map>
#include <algorithm>

#include "Tracer.h"

using std::cout;
using std::endl;
using std::map;
using std::pair;

void PrintOut(const pair<Tracer, Tracer>& p) {
  cout << "printout [" << p.first << "," << p.second << "]" << endl;
}

int main(int argc, char** argv) {
  Tracer a, b, c, d, e, f;
  map<Tracer, Tracer> table;
  map<Tracer, Tracer>::iterator it;

  table.insert(pair<Tracer, Tracer>(a, b));
  table[c] = d;
  table[e] = f;

  cout << "table[e]:" << table[e] << endl;
  it = table.find(c);
  cout << "PrintOut(*it), where it = table.find(c)" << endl;
  PrintOut(*it);

  cout << "iterating:" << endl;
  for_each(table.begin(), table.end(), PrintOut);

  return EXIT_SUCCESS;
}

animals.cc

#include <iostream>
#include <map>
#include <algorithm>

using std::cout;
using std::endl;
using std::map;
using std::pair;
using std::string;

void PrintOut(const pair<string, string>& p) {
  cout << p.first << " says " << p.second << endl;
}

// For those curious... https://youtu.be/jofNR_WkoCE
// ... enjoy?
int main(int argc, char** argv) {
  map<string, string> animals;

  animals["dog"]  = "woof";
  animals["cat"]  = "meow";
  animals["fish"] = "blub";      // originally ""
  animals["seal"] = "ow ow ow";  // originally "honk"
  animals["fox"];

  auto it = animals.find("fox");

  if (it == animals.end()) {
    cout << "fox not found" << endl;
  } else {
    cout << "fox found!" << endl;
  }

  cout << "iterating:" << endl;
  for_each(animals.begin(), animals.end(), &PrintOut);

  return EXIT_SUCCESS;
}

Makefile

CC = g++
CFLAGS = -Wall -g -std=c++17
PROGS = functiontemplate functiontemplate_infer valtemplate usepair \
    vectorfun vectoriterator vectoriterator_2011 vectoralgos mapexample listexample animals

all: $(PROGS)

functiontemplate: functiontemplate.cc
    $(CC) $(CFLAGS) -o $@ $^

functiontemplate_infer: functiontemplate_infer.cc
    $(CC) $(CFLAGS) -o $@ $^

# valarray example with non-type parameter N
valtemplate: valtemplate.cc
    $(CC) $(CFLAGS) -o $@ $^

# template class Pair example
usepair: usepair.cc Pair.h Pair.cc
    $(CC) $(CFLAGS) -o $@ $<

# phony target to compile vectorfun.o with CAP defined
vectorcap: vectorfun.cc
    $(CC) $(CFLAGS) -DCAP -c $<

# vector example
vectorfun: vectorfun.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

vectorfun.o: vectorfun.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

Tracer.o: Tracer.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# iterator example
vectoriterator: vectoriterator.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

vectoriterator.o: vectoriterator.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# iterator example with auto, range for
vectoriterator_2011: vectoriterator_2011.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

vectoriterator_2011.o: vectoriterator_2011.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# algorithms example (sort, for-each)
vectoralgos: vectoralgos.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

vectoralgos.o: vectoralgos.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# list example
listexample: listexample.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

listexample.o: listexample.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# map example
mapexample: mapexample.o Tracer.o
    $(CC) $(CFLAGS) -o $@ $^

mapexample.o: mapexample.cc Tracer.h
    $(CC) $(CFLAGS) -c $<

# animals live-coding
animals: animals.cc
    $(CC) $(CFLAGS) -o $@ $<

clean:
    rm -f *.o $(PROGS)