Class templates allow you to define a generic blueprint from which you can create specific instances of classes with different data types.
template <typename T> // added
class Array
{
private:
T* m_array;
int m_size;
public:
Array(int size)
: m_size(size)
{
m_array = new T[m_size];
}
~Array()
{
delete[] m_array;
}
T& operator[](int index)
{
return m_array[index];
}
int getSize() const
{
return m_size;
}
};
When defining member functions outside the class, you need to repeat the template declaration.
template <typename T>
T& Array<T>::operator[](int index)
{
return m_array[index];
}
When template class is in a header file and the definition of member functions is in a .cpp file:
A non-type template parameter is a template parameter where the type is predefined and is substituted for a constexpr value passed as an argument.
template <typename T, int size> // size is an integral non-type parameter
class StaticArray
{
private:
T m_array[size];
public:
T& operator[](int index)
{
return m_array[index];
}
int getSize() const
{
return size;
}
};
Full specialization is a way to provide a specific implementation for a particular type.
// Primary template
template <typename T>
void print(const T& t)
{
std::cout << t << '\n';
}
// Full specialization for type double
template<> // template parameter declaration containing no template parameters
void print<double>(const double& d) // specialized for type double
{
std::cout << std::scientific << d << '\n';
}
Full specializations are not implicitly inline. If you put a full specialization in a header file, it should be marked as inline
to avoid ODR violations when included in multiple translation units.
Class template specializations are treated as completely independent classes.
template <>
class Storage8<bool>
{
private:
unsigned char m_data;
public:
void set(int index, bool value)
{
if (value)
m_data |= (1 << index);
else
m_data &= ~(1 << index);
}
bool get(int index) const
{
return (m_data & (1 << index)) != 0;
}
};
If you explicitly define some function for a specific type (instead of a template class), the compiler will deduce the template class that is needed for this function.
template<>
void Storage<double>::print() // not inline
{
std::cout << std::scientific << m_value << '\n';
}
Partial template specialization allows you to specialize part of a template.
template <int size> // size is still a template non-type parameter
void print(const StaticArray<char, size>& array) // we're explicitly defining type char here
{
for (int count{ 0 }; count < size; ++count)
std::cout << array[count];
}
// Partial specialization of a class template
template <typename T>
class Storage<T*> // This is partially specialized for T*
{
private:
T* m_value;
public:
Storage(T* value)
: m_value(value)
{
}
~Storage()
{
delete m_value;
}
void print() const
{
std::cout << *m_value << '\n';
}
};
Functions cannot be partially specialized.
Class templates provide a powerful way to create generic classes that can work with any data type. Understanding how to define templates, specialize them, and use non-type template parameters is crucial for writing flexible and reusable code.