Шаблоны классов. Полная специализация. Частичная специализация
Перед изучением данной темы рекомендуется ознакомиться со следующей темой:
Содержание
Поиск на других ресурсах:
1. Концепция полной и частичной специализации. Общая форма
В самом простом случае, объявление шаблонного класса, оперирующего обобщенным типом T, выглядит следующим образом:
template <class T> class ClassName { // тело класса // ... }
здесь
- ClassName – имя шаблонного класса;
- T – обобщенное имя типа, которое может быть использовано в классе ClassName. Это имя может быть и другим.
На основе вышеуказанного шаблона можно объявлять конкретные экземпляры класса, непосредственно привязанные к некоторому типу.
ClassName<int> objInt; // формируется класс для типа int ClassName<double> objDouble; // формируется класс для типа double
Экземпляры класса objInt и objDouble формируются на этапе компиляции. Такой способ создания кода является частным случаем статического полиморфизма.
В языке C++ допускается объявлять так называемую «специализацию» шаблонного класса. Специализация позволяет отредактировать создание экземпляра обобщенного класса для особых случаев. Такими особыми случаями могут быть:
- запрет использования класса для некоторых типов. Например, если класс оперирует числами, то строковые типы здесь неуместны (невозможно выполнять математические вычисления над символами);
- предусмотрена реализация для указателя на обобщенный тип. Например, для обобщенного типа T нужно реализовать объявление указателя на этот тип (T*) с учетом того, что тип char* может быть строкой символов и обрабатывается по особому;
- другие случаи.
Специализация шаблонного класса может быть двух видов:
- полная специализация;
- частичная специализация.
Полная специализация имеет следующую общую форму:
template <> class ClassName<type> { // тело класса // ... }
здесь
- ClassName – имя класса, для которого объявляется полная специализация. Важно: перед объявлением полной специализации класс должен быть объявлен как шаблонный;
- type – тип, определяющий специализацию шаблонного типа, объявленного в шаблоне класса ClassName. Тип type может быть любым стандартным типом (int, float, double и т.д.) или типом, определенным в программе.
Учитывая вышеприведенную общую форму, объявление экземпляра класса при полной специализации может быть таким
ClassName<type> obj;
здесь
- obj – имя экземпляра (объекта) класса;
- type – тип, определяемый при объявлении полной специализации класса.
Частичная специализация имеет следующую общую форму:
template <class T> class ClassName<specialization_T> { // тело класса // ... }
здесь
- ClassName – имя шаблонного класса, для которого определяется частичная специализация. Важно: шаблон класса ClassName должен быть обязательно объявлен в программе;
- T – тип класса, который определен в шаблоне;
- specialization_T – специализация типа T. Это может быть, например, указатель типа T, который определен как T*.
Учитывая вышеприведенную общую форму частичной специализации, объявление объекта класса может быть следующим:
ClassName<specialization_T> obj;
здесь
- obj – имя объекта класса;
- specialization_T – специализация типа T (например T*, T** и т.д.);
- ClassName – имя шаблонного класса.
⇑
2. Пример реализации полной и частичной специализации. Класс Value<T>
В примере демонстрируются:
- объявление шаблонного класса Value<T>;
- полная специализация класса Value<T> для типа char*;
- полная специализация класса Value<T> для типа void*;
- частичная специализация класса Value<T> для типа T* — указатель на любой тип кроме char*.
#include <iostream> using namespace std; // Тема: Шаблонные классы. Полная специализация. Частичная специализация. // 1. Обычное объявление шаблонного класса // Задан шаблонный класс Value<T>, // сохраняющий некоторое значение. template <class T> class Value { private: T value; public: // Конструктор с 1 параметром Value(T value = 0) : value(value) { } // Метод тестирования void Print(string msg) { cout << msg << " = " << value << endl; } // Метод, возвращающий значение T Get() { return value; } }; // 2. Полная специализация для типа char* // Полная специализация для типа char*, // признаком полной специализации является строка template<> template <> class Value<char*> { private: char* s; // Копирование s <= _s void Copy(const char* _s) { int len = 0; while (_s[len++] != '\0'); s = new char[len + 1]; for (int i = 0; i <= len; i++) s[i] = _s[i]; } public: // Конструктор Value(const char* _s = nullptr) { // Копирование s <= _s if (_s != nullptr) Copy(_s); } // Метод тестирования void Print(string msg) { cout << msg << " => " << s << endl; } // Деструктор ~Value() { delete[] s; } // Конструктор копирования – делегирует полномочия конструктору с 1 параметром Value(const Value<char*>& obj) : Value<char*>(obj.s) { } // Оператор копіювання Value& operator=(const Value<char*>& obj) { // 1. Освободить память, выделенную под предыдущую строку delete[] s; // 2. Выделить память под новую строку, копирование s<=obj.s Copy(obj.s); // 3. Вернуть текущий объект return *this; } // Метод Get() - возвращает указатель на char char* Get() { return s; } }; // 3. Полная специализация для void*, // здесь параметры шаблона <> отсутствуют так же template <> class Value<void*> { private: void* p; public: Value() { p = nullptr; } Value(void* _p) : p(_p){ } // Метод Get() void* Get() { return p; } }; // 4. Частичная специализация, // которая используется для всех указателей типа T template <class T> class Value<T*> //: private Value<void*> // все указатели void* можно привести к любому T* { private: T* p; public: Value(T* p) { // делаем копию this->p = new T; *(this->p) = *p; } void Print(string msg) { cout << msg << " = " << *p << endl; } // Конструктор копирования Value(const Value& obj) : Value(obj.p) {} // Оператор копирования Value& operator=(const Value& obj) { *p = *obj.p; return *this; } // Деструктор ~Value() { delete p; } // Метод Get() - не возвращает копию T* Get() { return p; } }; int main() { // Тест класса Value<T> // 1. Обычное объявление шаблонного класса Value<int> vi1 = 22; vi1.Print("vi1"); // 2. Полная специализация для типа char* // 2.1. Объявить экземпляр Value<char*> vc1 = "Hello, world!"; vc1.Print("vc1"); // 2.2. Тест конструктора копирования Value<char*> vc2 = vc1; vc2.Print("vc2"); // 2.3. Тест оператора копирования Value<char*> vc3; vc3 = vc1; vc3.Print("vc3"); // 2.4. Метод Get() char* vc4 = vc2.Get(); cout << "vc4 = " << vc4 << endl; // 3. Полная специализация для типа void* // Здесь вызывается полная специализация для void* Value<void*> p2; void* pvoid = p2.Get(); // 4. Частичная специализация для типа int* // 4.1. Объявить экземпляр Value<int*> pi1 = new int(50); pi1.Print("pi1"); // 4.2. Тест конструктора копирования Value<int*> pi2 = pi1; pi2.Print("pi2"); // 4.3. Тест оператора копирования pi2 = pi1; pi2.Print("pi2"); // 4.4. Метод Get() int* pi = pi2.Get(); cout << "pi = " << *pi << endl; }
Связанные темы
⇑