Template Class Method Definitions
However, where should these class template member function definitions be placed? If they are placed in a separate .cpp file (which is conventional with regular classes), how would the member function definitions be instantiated, and associated with the instantiated template class?
From what we’ve learned about C++, so far, you’d suspect that you could separate out the class member function implementation (definition) from the declaration.
That depends on the Template Compilation Model:
Template Compilation Models
The answer depends on the Template Compilation Model of your C++ compiler:
If it uses the Inclusion Compilation Model, then you must place the definitions in an appropriate header file, and include that header file wherever a class template needs to be instantiated.
Originally, C++ included a Separation Compilation Model, which would allow you to separate out definitions and declaration, and added some syntax (an extra
exportkeyword in the template declaration) to support it. However, that was removed in C++11.C++20 introduced modules as a better alternative for separate compilation.
Template Parameters
As with function templates, class templates have a template parameter list that contains one or more template parameters. A template parameter may be one of two types:
A template type parameter:
typename T or class T
where T is simply a name for the type. The actual type is determined by the compiler when it instantiates the template class.
A template nontype parameter:
int size
or
double value
etc...
The expression to which a nontype parameter is bound must be a constant expression (i.e. it must be possible to evaluate it at compile time).
For example:
template <typename Type, int size>
class Buffer;
Here’s an example, a character array (CharArray), where the nontype parameter specifies the the number of elements in the array:
//
// CharArray.h
// Template Nontype Parameters
//
// Created by Bryan Higgs on 10/17/24.
//
#ifndef CharArray_h
#define CharArray_h
#include <assert.h>
template <int SIZE>
class CharArray
{
public:
size_t getSize() { return SIZE; }
char &operator[](int i)
{
assert(i >= 0 && i < SIZE);
return m_buffer[i];
}
private:
char m_buffer[SIZE];
};
#endif /* CharArray_h *///
// main.cpp
// Template Nontype Parameters
//
// Created by Bryan Higgs on 10/17/24.
//
#include <iostream>
#include "CharArray.h"
int main(int argc, const char * argv[])
{
CharArray<8> carray1;
for (int i = 0; i < carray1.getSize(); i++)
{
carray1[i] = 'a' + i;
}
std::cout << "Array has " << carray1.getSize()
<< " elements" << std::endl;
for (int i = 0; i < carray1.getSize(); i++)
{
std::cout << "[" << i << "] : "
<< carray1[i] << std::endl;
}
return 0;
}which outputs:
Array has 8 elements
[0] : a
[1] : b
[2] : c
[3] : d
[4] : e
[5] : f
[6] : g
[7] : h
Program ended with exit code: 0
How about a Queue class template?
//
// Queue.h
// Queue Template Class
//
// Created by Bryan Higgs on 10/17/24.
//
#ifndef Queue_h
#define Queue_h
#include "assert.h"
// Forward declaration
template <typename TYPE>
class QueueItem;
// The Queue
template <typename TYPE>
class Queue
{
public:
typedef void func_t(QueueItem<TYPE> *qiPtr);
Queue()
: m_front(0), m_back(0)
{}
~Queue()
{
while (!IsEmpty())
RemoveFromFront();
}
void AddToBack(const TYPE &value)
{
QueueItem<TYPE> *qiPtr
= new QueueItem<TYPE>(value);
if (IsEmpty())
m_front = m_back = qiPtr;
else
{
m_back->setNext(qiPtr);
m_back = qiPtr;
}
}
const TYPE RemoveFromFront()
{
if(IsEmpty())
return 0; // Nothing to remove
QueueItem<TYPE> *qiPtr = m_front;
m_front = m_front->getNext();
const TYPE theValue = qiPtr->getValue();
delete qiPtr; // Deallocate the QueueItem
return theValue;
}
bool IsEmpty() const
{
return m_front == 0;
}
void ForEachItem(func_t *func)
{
for (QueueItem<TYPE> *qi = m_front;
qi != 0; qi = qi->getNext())
{
func(qi);
}
}
private:
QueueItem<TYPE> *m_front;
QueueItem<TYPE> *m_back;
};
// A Queue item
template <typename TYPE>
class QueueItem
{
public:
QueueItem(const TYPE &item)
: m_item(item), m_next(0)
{}
const TYPE getValue() const
{
return m_item;
}
QueueItem* getNext()
{
return m_next;
}
void setNext(QueueItem* next)
{
m_next = next;
}
private:
const TYPE m_item;
QueueItem *m_next;
};
#endif /* Queue_h *///
// main.cpp
// Queue Template Class
//
// Created by Bryan Higgs on 10/17/24.
//
#include <iostream>
#include "Queue.h"
void displayItem( QueueItem<int> *qiPtr )
{
std::cout << "QueueItem: " << qiPtr
<< " value = "
<< qiPtr->getValue() << std::endl;
}
int main(int argc, const char * argv[])
{
Queue<int> queue;
for (int i = 0; i < 5; i++)
{
queue.AddToBack(i*i);
// The square of i
}
queue.ForEachItem(displayItem);
for (int i = 0; i < 5; i++)
{
const int value
= queue.RemoveFromFront();
std::cout << "[" << i << "] : "
<< value << std::endl;
}
return 0;
}
//
// main.cpp
// Template Nontype Parameters
//
// Created by Bryan Higgs on 10/17/24.
//
#include <iostream>
#include "CharArray.h"
int main(int argc, const char * argv[])
{
CharArray<8> carray1;
for (int i = 0; i < carray1.getSize(); i++)
{
carray1[i] = 'a' + i;
}
std::cout << "Array has " << carray1.getSize()
<< " elements" << std::endl;
for (int i = 0; i < carray1.getSize(); i++)
{
std::cout << "[" << i << "] : "
<< carray1[i] << std::endl;
}
return 0;
}which outputs:
QueueItem: 0x600000008020 value = 0
QueueItem: 0x600000008030 value = 1
QueueItem: 0x600000008040 value = 4
QueueItem: 0x600000008050 value = 9
QueueItem: 0x600000008060 value = 16
[0] : 0
[1] : 1
[2] : 4
[3] : 9
[4] : 16
Program ended with exit code: 0