SyntaxStudy
Sign Up
C++ Class Templates and Template Specialisation
C++ Beginner 1 min read

Class Templates and Template Specialisation

Class templates generalise class definitions over one or more type parameters, enabling data structures and algorithms that work uniformly across types while remaining fully type-safe. 'std::vector', 'std::map', and 'std::unique_ptr' are all class templates. Defining your own class template follows the same syntax as function templates but applies to the entire class body including all member functions. Full template specialisation provides a completely different implementation of a template for a specific set of arguments. This is useful when a general algorithm can be optimised for a particular type — for example, a sorting algorithm might have a specialisation for integers that uses counting sort. The specialisation must appear after the primary template declaration. Partial template specialisation is available only for class templates (not function templates). It allows specialising on a subset of the template parameters — for instance, specialising a container for pointer types. Partial specialisation drives much of the machinery behind standard library type traits such as 'std::is_pointer' and 'std::remove_const'.
Example
#include <iostream>
#include <string>

// Primary class template
template<typename T>
class TypeInfo {
public:
    static std::string name()    { return "unknown";  }
    static bool isPointer()      { return false;      }
};

// Full specialisation for int
template<>
class TypeInfo<int> {
public:
    static std::string name()    { return "int";      }
    static bool isPointer()      { return false;      }
};

// Full specialisation for double
template<>
class TypeInfo<double> {
public:
    static std::string name()    { return "double";   }
    static bool isPointer()      { return false;      }
};

// Partial specialisation for any pointer type
template<typename T>
class TypeInfo<T*> {
public:
    static std::string name()    { return TypeInfo<T>::name() + "*"; }
    static bool isPointer()      { return true;       }
};

// Generic Pair class template
template<typename First, typename Second>
class Pair {
public:
    Pair(First f, Second s) : first(f), second(s) {}
    First  first;
    Second second;
    void print() const {
        std::cout << "(" << first << ", " << second << ")\n";
    }
};

int main() {
    std::cout << TypeInfo<int>::name()     << "\n";  // int
    std::cout << TypeInfo<double>::name()  << "\n";  // double
    std::cout << TypeInfo<int*>::name()    << "\n";  // int*
    std::cout << TypeInfo<float>::name()   << "\n";  // unknown
    std::cout << std::boolalpha
              << TypeInfo<int*>::isPointer() << "\n"; // true

    Pair<std::string, int> p("age", 30);
    p.print();

    return 0;
}