This is the most common approach.
Generate the code for the instantiation in every translation unit and letting the linker weed out duplicates through the header guards.
// file: Stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include <iostream>
template<class T>
class Stack {
private:
int size; // # of elements in the stack
int top; // location of the top element
T *stackPtr; // pointer to the stack
public:
Stack(int=10); // default constructor (stack size=10)
~Stack() { delete [] stackPtr;}
bool push(const T&); // push an element onto the stack
bool pop(T&); // pop an element off the stack
private:
bool isFull() const {return top == size-1;}
bool isEmpty() const {return top == -1;}
};
#endif
// file: Stack.cpp
#include "Stack.h"
template<class T>
Stack<T>::Stack(int s)
{
size = s > 0 ? s : 10; // stack is initially empty
top = -1;
stackPtr = new T[size];
}
template<class T>
bool Stack<T>::push(const T& val)
{
if(!isFull()) {
stackPtr[++top] = val;
return true;
}
return false;
}
template<class T>
bool Stack<T>::pop(T& val)
{
if(!isEmpty()) {
val = stackPtr[top--];
return true;
}
return false;
}
// file: TestStack.cpp
#include <iostream>
#include "Stack.h"
#include "Stack.cpp"
using namespace std;
template<class T>
void testStack(Stack<T>& theStack, T value, T increment, const char* stackName)
{
cout << "\nPushing elements onto " << stackName << endl;
while (theStack.push(value)) {
cout << value << " ";
value += increment;
}
cout << "\nStack is full, cannot push " << value;
cout << "\n\nPop elements from " << stackName << endl;
while(theStack.pop(value)) cout << value << " " ;
cout << "\nStack is empty, Cannot pop\n";
}
int main()
{
Stack<double> doubleStack(5);
Stack<int> intStack;
testStack(doubleStack, 1.1, 1.1, "doubleStack");
testStack(intStack,1,1, "intStack");
return 0;
}
OurMin.h: contains the declaration of the min( ) function template.
OurMin.cpp: contains the definition of the min( ) function template.
UseMin1.cpp: attempts to use an int-instantiation of min( ).
UseMin2.cpp: attempts to use an int-instantiation of min( ).
MinMain.cpp: calls usemin1( ) and usemin2( ).
// file: OurMin.h declaring min()
#ifndef OURMIN_H
#define OURMIN_H
template<typename T> const T& min(const T&, const T&);
#endif // OURMIN_H
// file: OurMin.cpp defining min()
#include "OurMin.h"
template<typename T>
const T& min(const T& a, const T& b)
{
return (a < b) ? a : b;
}
//file:UseMin1.cpp
#include <iostream>
#include "OurMin.h"
#include "OurMin.cpp"
void usemin1()
{
std::cout << min(1,2) << std::endl;
}
//file:UseMin2.cpp
#include <iostream>
#include "OurMin.h"
#include "OurMin.cpp"
void usemin2()
{
std::cout << min(3,4) << std::endl;
}
//file:MinMain.cpp
void usemin1();
void usemin2();
int main()
{
usemin1();
usemin2();
}
$ ~ g++ MinMain.cpp UseMin1.cpp UseMin2.cpp
Duplicated definitions:
Most compilers can deal with this.
All template source code is visible to the client, so there is little opportunity for library vendors to hide their implementation strategies.
Header files tend to be much larger than they would be if function bodies were compiled separately.
This can increase compile times dramatically over traditional compilation models.
Here the function template definitions are separated from their declarations across translation units.
The translation unit is defined as the code in a file, including header information, but excluding compilation dependent sections such as where we have #ifndef statements.
This separation is done by exporting templates.
The keyword export is not supported by all compilers.
The separation model:
// file:OurMin.h Declares min as an exported template
#ifndef OURMIN_H
#define OURMIN_H
export template<typename T> const T& min(const T&, const T&);
#endif
// file:OurMin.cpp Definition of the exported min template
#include "OurMin.h"
export template<typename T> const T& min(const T& a, const T& b)
{
return (a < b) ? a : b;
}
This doesn’t work often and probably shouldn’t be used anyway.
There isn’t wide support for export
You can manually direct the compiler to instantiate any template specializations of your choice.
When you use this technique, there must be one and only one such directive for each such specialization; otherwise you might get multiple definition errors.
Consider the following example that consists of five files:
OurMin.h: contains the declaration of the min() function template.
OurMin.cpp: contains the definition of the min() function template.
UseMin1.cpp: attempts to use an int-instantiation of min().
UseMin2.cpp: attempts to use a double-instantiation of min().
MinMain.cpp: calls usemin1() and usemin2().
We are going to try to remove the dependence on OurMin.cpp from the UseMin1.cpp and UseMin2.cpp files, by removing the inclusion.
// file: OurMin.h declaring min()
#ifndef OURMIN_H
#define OURMIN_H
template<typename T> const T& min(const T&, const T&);
#endif // OURMIN_H
// file: OurMin.cpp defining min()
#include "OurMin.h"
template<typename T> const T& min(const T& a, const T& b)
{
return (a < b) ? a : b;
}
// file: UseMin1.cpp
#include <iostream>
#include "OurMin.h"
void usemin1()
{
std::cout << min(1, 2) << std::endl;
}
// file: UseMin2.cpp
#include <iostream>
#include "OurMin.h"
void usemin2()
{
std::cout << min(3.1, 4.2) << std::endl;
}
// file: MinMain.cpp
void usemin1();
void usemin2();
int main() {
usemin1();
usemin2();
}
$ ~ g++ MinMain.cpp UseMin1.cpp UseMin2.cpp
/tmp/ccJ7l6tH.o: In function `usemin1()':
UseMin1.cpp:(.text+0x34): undefined reference to `int const& min<int>(int const&, int const&)'
/tmp/ccQeGJT1.o: In function `usemin2()': UseMin2.cpp:(.text+0x34): undefined reference to `int const& min<int>(int const&, int const&)' collect2: error: ld returned 1 exit status
Linker errors: Unresolved external references for min<int> and min<double>
$ ~ g++ UseMin1.cpp UseMin2.cpp MinMain.cpp
/tmp/ccbJpZ8c.o: In function `usemin1()': UseMin1.cpp:(.text+0x34): undefined reference to `int const& min<int>(int const&, int const&)'
/tmp/ccLjsPaf.o: In function `usemin2()': UseMin2.cpp:(.text+0x34): undefined reference to `int const& min<int>(int const&, int const&)' collect2: error: ld returned 1 exit status
To manage the linker errors, we introduce a new file, MinInstances.cpp, which explicitly instantiates the needed instantiations of min( ):
// file: MinInstances.cpp
#include "OurMin.cpp" // Explicit Instantiations for int and double
template const int& min<int>(const int&, const int&);
template const double& min<double>(const double&, const double&);
// file: OurMin.cpp defining min()
#ifndef _OURMIN_CPP_
#define _OURMIN_CPP_
#include "OurMin.h"
template<typename T>
const T& min(const T& a, const T& b)
{
return (a < b) ? a : b;
}
#endif _OURMIN_CPP_
$ ~ g++ Minmain.cpp UseMin1.cpp UseMin2.cpp MinInstances.cpp
We don’t get multiple duplicated definitions anymore, so compilers should be able to deal with this.
We can produce template functions even if we aren’t specifically using them, which may be useful for distributing a library with built code.
On the downside, we need to explicitly put the appropriate types into MinInstances.cpp