Специализация шаблона (C++)
Шаблоны класса можно частично специализировать, при этом получившийся класс по-прежнему будет шаблоном. Частичная специализация позволяет частично настроить код шаблона для определенных типов, например:
Шаблон имеет несколько типов, и только некоторые из них требуют специализации. Результат для остальных типов параметризован шаблоном.
Шаблон имеет только один тип, но специализация необходима для типов указателя, ссылки, указателя на член или указателя на функцию. Специализация сама по себе по-прежнему является шаблоном с типом, на который задан указатель или ссылка.
Пример: частичная специализация шаблонов классов
// partial_specialization_of_class_templates.cpp
#include <stdio.h>
template <class T> struct PTS {
enum {
IsPointer = 0,
IsPointerToDataMember = 0
};
};
template <class T> struct PTS<T*> {
enum {
IsPointer = 1,
IsPointerToDataMember = 0
};
};
template <class T, class U> struct PTS<T U::*> {
enum {
IsPointer = 0,
IsPointerToDataMember = 1
};
};
struct S{};
int main() {
printf_s("PTS<S>::IsPointer == %d \nPTS<S>::IsPointerToDataMember == %d\n",
PTS<S>::IsPointer, PTS<S>:: IsPointerToDataMember);
printf_s("PTS<S*>::IsPointer == %d \nPTS<S*>::IsPointerToDataMember == %d\n"
, PTS<S*>::IsPointer, PTS<S*>:: IsPointerToDataMember);
printf_s("PTS<int S::*>::IsPointer == %d \nPTS"
"<int S::*>::IsPointerToDataMember == %d\n",
PTS<int S::*>::IsPointer, PTS<int S::*>::
IsPointerToDataMember);
}
PTS<S>::IsPointer == 0
PTS<S>::IsPointerToDataMember == 0
PTS<S*>::IsPointer == 1
PTS<S*>::IsPointerToDataMember == 0
PTS<int S::*>::IsPointer == 0
PTS<int S::*>::IsPointerToDataMember == 1
Пример: частичная специализация для типов указателей
Если у вас есть класс коллекции шаблонов, который принимает любой тип T
, можно создать частичную специализацию, которая принимает любой тип T*
указателя. В следующем коде показан шаблон класса коллекции Bag
и частичная специализация для типов указателей, в которой коллекция разыменовывает типы указателей перед их копированием в массив. Затем коллекция сохраняет значения, на которые указывают указатели. В исходном шаблоне в коллекции сохранялись бы только сами указатели, а данные оставались бы уязвимы в отношении удаления или изменения. В этой версии коллекции, специально предназначенной для указателей, добавлен код для проверки наличия пустых указателей null в методе add
.
// partial_specialization_of_class_templates2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
// Original template collection class.
template <class T> class Bag {
T* elem;
int size;
int max_size;
public:
Bag() : elem(0), size(0), max_size(1) {}
void add(T t) {
T* tmp;
if (size + 1 >= max_size) {
max_size *= 2;
tmp = new T [max_size];
for (int i = 0; i < size; i++)
tmp[i] = elem[i];
tmp[size++] = t;
delete[] elem;
elem = tmp;
}
else
elem[size++] = t;
}
void print() {
for (int i = 0; i < size; i++)
cout << elem[i] << " ";
cout << endl;
}
};
// Template partial specialization for pointer types.
// The collection has been modified to check for NULL
// and store types pointed to.
template <class T> class Bag<T*> {
T* elem;
int size;
int max_size;
public:
Bag() : elem(0), size(0), max_size(1) {}
void add(T* t) {
T* tmp;
if (t == NULL) { // Check for NULL
cout << "Null pointer!" << endl;
return;
}
if (size + 1 >= max_size) {
max_size *= 2;
tmp = new T [max_size];
for (int i = 0; i < size; i++)
tmp[i] = elem[i];
tmp[size++] = *t; // Dereference
delete[] elem;
elem = tmp;
}
else
elem[size++] = *t; // Dereference
}
void print() {
for (int i = 0; i < size; i++)
cout << elem[i] << " ";
cout << endl;
}
};
int main() {
Bag<int> xi;
Bag<char> xc;
Bag<int*> xp; // Uses partial specialization for pointer types.
xi.add(10);
xi.add(9);
xi.add(8);
xi.print();
xc.add('a');
xc.add('b');
xc.add('c');
xc.print();
int i = 3, j = 87, *p = new int[2];
*p = 8;
*(p + 1) = 100;
xp.add(&i);
xp.add(&j);
xp.add(p);
xp.add(p + 1);
delete[] p;
p = NULL;
xp.add(p);
xp.print();
}
10 9 8
a b c
Null pointer!
3 87 8 100
Пример. Определение частичной специализации таким образом, чтобы один тип был int
В следующем примере определяется класс шаблона, принимаюющий пары всех двух типов, а затем определяет частичную специализацию этого класса шаблона, специализированного для одного из типов int
. Специализация определяет дополнительный метод сортировки, реализующий простую сортировку пузырьков на основе целого числа.
// partial_specialization_of_class_templates3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
template <class Key, class Value> class Dictionary {
Key* keys;
Value* values;
int size;
int max_size;
public:
Dictionary(int initial_size) : size(0) {
max_size = 1;
while (initial_size >= max_size)
max_size *= 2;
keys = new Key[max_size];
values = new Value[max_size];
}
void add(Key key, Value value) {
Key* tmpKey;
Value* tmpVal;
if (size + 1 >= max_size) {
max_size *= 2;
tmpKey = new Key [max_size];
tmpVal = new Value [max_size];
for (int i = 0; i < size; i++) {
tmpKey[i] = keys[i];
tmpVal[i] = values[i];
}
tmpKey[size] = key;
tmpVal[size] = value;
delete[] keys;
delete[] values;
keys = tmpKey;
values = tmpVal;
}
else {
keys[size] = key;
values[size] = value;
}
size++;
}
void print() {
for (int i = 0; i < size; i++)
cout << "{" << keys[i] << ", " << values[i] << "}" << endl;
}
};
// Template partial specialization: Key is specified to be int.
template <class Value> class Dictionary<int, Value> {
int* keys;
Value* values;
int size;
int max_size;
public:
Dictionary(int initial_size) : size(0) {
max_size = 1;
while (initial_size >= max_size)
max_size *= 2;
keys = new int[max_size];
values = new Value[max_size];
}
void add(int key, Value value) {
int* tmpKey;
Value* tmpVal;
if (size + 1 >= max_size) {
max_size *= 2;
tmpKey = new int [max_size];
tmpVal = new Value [max_size];
for (int i = 0; i < size; i++) {
tmpKey[i] = keys[i];
tmpVal[i] = values[i];
}
tmpKey[size] = key;
tmpVal[size] = value;
delete[] keys;
delete[] values;
keys = tmpKey;
values = tmpVal;
}
else {
keys[size] = key;
values[size] = value;
}
size++;
}
void sort() {
// Sort method is defined.
int smallest = 0;
for (int i = 0; i < size - 1; i++) {
for (int j = i; j < size; j++) {
if (keys[j] < keys[smallest])
smallest = j;
}
swap(keys[i], keys[smallest]);
swap(values[i], values[smallest]);
}
}
void print() {
for (int i = 0; i < size; i++)
cout << "{" << keys[i] << ", " << values[i] << "}" << endl;
}
};
int main() {
Dictionary<const char*, const char*> dict(10);
dict.print();
dict.add("apple", "fruit");
dict.add("banana", "fruit");
dict.add("dog", "animal");
dict.print();
Dictionary<int, const char*> dict_specialized(10);
dict_specialized.print();
dict_specialized.add(100, "apple");
dict_specialized.add(101, "banana");
dict_specialized.add(103, "dog");
dict_specialized.add(89, "cat");
dict_specialized.print();
dict_specialized.sort();
cout << endl << "Sorted list:" << endl;
dict_specialized.print();
}
{apple, fruit}
{banana, fruit}
{dog, animal}
{100, apple}
{101, banana}
{103, dog}
{89, cat}
Sorted list:
{89, cat}
{100, apple}
{101, banana}
{103, dog}
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по