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)