COMP6771 Week 7.1
Templates, Part 2
Inclusion compilation model
- What is wrong with this?
- g++ min.cpp main.cpp -o main
template <typename T>
T min(T a, T b);
template <typename T>
T min(T a, T b) {
return a < b ? a : b;
}
#include <iostream>
int main() {
std::cout << min(1, 2) << "\n";
}
min.h
min.cpp
main.cpp
Inclusion compilation model
- When it comes to templates, we include definitions (i.e. implementation) in the .h file
- This is because template definitions need to be known at compile time (template definitions can't be instantiated at link time because that would require an instantiation for all types)
- Will expose implementation details in the .h file
- Can cause slowdown in compilation as every file using min.h will have to instantiate the template, then it's up the linker to ensure there is only 1 instantiation.
template <typename T>
T min(T a, T b) {
return a < b ? a : b;
}
#include <iostream>
int main() {
std::cout << min(1, 2) << "\n";
}
min.h
main.cpp
Inclusion Compilation Model
- Alternative: Explicit instantiations
- Generally a bad idea
template <typename T>
T min(T a, T b);
template <typename T>
T min(T a, T b) {
return a < b ? a : b;
}
template int min<int>(int, int);
template double min<double>(double, double);
#include <iostream>
int main() {
std::cout << min(1, 2) << "\n";
std::cout << min(1.0, 2.0) << "\n";
}
min.h
min.cpp
main.cpp
Inclusion Compilation Model
- Exact same principles will apply for classes
- Implementations must be in header file, and compiler should only behave as if one Stack<int> was instantiated
#include <vector>
template <typename T>
class Stack {
public:
Stack() {}
void pop();
void push(const T& i);
private:
std::vector<T> items_;
}
#include "stack.tpp"
template <typename T>
void Stack<T>::pop() {
items_.pop_back();
}
template <typename T>
void Stack<T>::push(const T& i) {
items_.push_back(i);
}
int main() {
Stack<int> s;
}
Stack.h
Stack.tpp
main.cpp
Inclusion Compilation Model
- Lazy instantiation: Only members functions that are called are instantiated
- In this case, pop() will not be instantiated
#include <vector>
template <typename T>
class Stack {
public:
Stack() {}
void pop();
void push(const T& i);
private:
std::vector<T> items_;
}
#include "stack.tpp"
template <typename T>
void Stack<T>::pop() {
items_.pop_back();
}
template <typename T>
void Stack<T>::push(const T& i) {
items_.push_back(i);
}
int main() {
Stack<int> s;
s.push(5);
}
Stack.h
Stack.tpp
main.cpp
Static Members
- Each template instantiation has it's own set of static members
#include <vector>
template <typename T>
class Stack {
public:
Stack();
~Stack();
void push(T&);
void pop();
T& top();
const T& top() const;
static int numStacks_;
private:
std::vector<T> stack_;
};
template <typename T>
int Stack<T>::numStacks_ = 0;
template <typename T>
Stack<T>::Stack() { numStacks_++; }
template <typename T>
Stack<T>:: ~Stack() { numStacks_--; }
#include <iostream>
#include "lectures/week7/stack.h"
int main() {
Stack<float> fs;
Stack<int> is1, is2, is3;
std::cout << Stack<float>::numStacks_ << "\n";
std::cout << Stack<int>::numStacks_ << "\n";
}
Friends
- Each stack instantiation has one unique instantiation of the friend
#include <vector>
template <typename T>
class Stack {
public:
Stack();
~Stack();
void push(T&);
void pop();
friend std::ostream& operator<<(std::ostream& os, const Stack& s);
private:
std::vector<T> stack_;
};
template <typename T>
void push(T& t) {
stack_.push_back(t);
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const Stack<T>& s) {
std::cout << "My top item is " << s.stack_.back() << \n";
}
#include <iostream>
#include <string>
#include "lectures/week7/stack.h"
int main() {
Stack<std::string> ss;
ss.push("Hello");
std::cout << ss << "\n":
Stack<int> is;
is.push(5);
std::cout << is << "\n":
}
Default Members
- We can provide default arguments to template types (where the defaults themselves are types)
- It means we have to update all of our template parameter lists
#include <vector>
template <typename T, typename CONT = std::vector<T>>
class Stack {
public:
Stack();
~Stack();
void push(T&);
void pop();
T& top();
const T& top() const;
static int numStacks_;
private:
CONT stack_;
};
template <typename T, typename CONT>
int Stack<T, CONT>::numStacks_ = 0;
template <typename T, typename CONT>
Stack<T, CONT>::Stack() { numStacks_++; }
template <typename T, typename CONT>
Stack<T, CONT>:: ~Stack() { numStacks_--; }
#include <iostream>
#include "lectures/week7/stack.h"
int main() {
Stack<float> fs;
Stack<int> is1, is2, is3;
std::cout << Stack<float>::numStacks_ << "\n";
std::cout << Stack<int>::numStacks_ << "\n";
}
COMP6771 19T2 - 7.1 - Templates Part 2
By cs6771
COMP6771 19T2 - 7.1 - Templates Part 2
- 985