Частичная специализация
Специализируйте шаблоны
«Частичная специализация» — бесплатный урок C++ Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Что такое частичная специализация
Частичная специализация настраивает шаблон для семейства типов с общей структурой, а не для одного конкретного типа.
- Она поддерживается только шаблонами классов.
- Некоторые параметры по-прежнему остаются обобщёнными.
#include <iostream>
template <typename T>
struct Traits { static const char* kind() { return "value"; } };
template <typename T>
struct Traits<T*> { static const char* kind() { return "pointer"; } };
int main() {
std::cout << Traits<int>::kind() << '\n';
std::cout << Traits<int*>::kind() << '\n';
return 0;
}Сопоставление указателей
Шаблон T* сопоставляется с любым типом указателя, поэтому все указатели можно обрабатывать одной специализацией.
#include <iostream>
template <typename T>
struct Deref { static T get(T v) { return v; } };
template <typename T>
struct Deref<T*> { static T get(T* p) { return *p; } };
int main() {
int x = 42;
std::cout << Deref<int>::get(7) << '\n';
std::cout << Deref<int*>::get(&x) << '\n';
return 0;
}Функции нельзя специализировать частично
Частичную специализацию поддерживают только шаблоны классов и переменных. Для функций вместо этого используется перегрузка.
#include <iostream>
template <typename T>
void show(T) { std::cout << "value\n"; }
template <typename T>
void show(T*) { std::cout << "pointer\n"; }
int main() {
int x = 0;
show(5);
show(&x);
return 0;
}Специализация одного из двух параметров
При наличии нескольких параметров-типов можно зафиксировать один, а другой оставить открытым, сопоставляя шаблон между разными типами.
#include <iostream>
template <typename A, typename B>
struct Pair { static const char* kind() { return "mixed"; } };
template <typename A>
struct Pair<A, A> { static const char* kind() { return "same types"; } };
int main() {
std::cout << Pair<int, double>::kind() << '\n';
std::cout << Pair<int, int>::kind() << '\n';
return 0;
}Сопоставление массивов
Типы массивов можно сопоставлять с помощью шаблона, который извлекает тип элемента и размер как параметр, не являющийся типом.
#include <iostream>
template <typename T>
struct Info { static int size() { return 1; } };
template <typename T, int N>
struct Info<T[N]> { static int size() { return N; } };
int main() {
std::cout << Info<int>::size() << '\n';
std::cout << Info<int[5]>::size() << '\n';
return 0;
}Шаблоны для const
Специализация может удалять или обнаруживать const, сопоставляя const T как шаблон.
#include <iostream>
template <typename T>
struct IsConst { static const bool value = false; };
template <typename T>
struct IsConst<const T> { static const bool value = true; };
int main() {
std::cout << std::boolalpha;
std::cout << IsConst<int>::value << '\n';
std::cout << IsConst<const int>::value << '\n';
return 0;
}Побеждает наиболее специализированный вариант
Если подходит несколько специализаций, компилятор выбирает наиболее специализированную — ту, чей шаблон является самым конкретным.
#include <iostream>
template <typename T>
struct K { static int v() { return 0; } };
template <typename T>
struct K<T*> { static int v() { return 1; } };
template <>
struct K<int*> { static int v() { return 2; } };
int main() {
std::cout << K<double*>::v() << ' ' << K<int*>::v() << '\n';
return 0;
}Шаблоны ссылок
Можно также сопоставлять ссылочные типы, отличая ссылку от обычного значения.
#include <iostream>
template <typename T>
struct Cat { static const char* k() { return "value"; } };
template <typename T>
struct Cat<T&> { static const char* k() { return "lvalue ref"; } };
int main() {
std::cout << Cat<int>::k() << '\n';
std::cout << Cat<int&>::k() << '\n';
return 0;
}Создание типовых признаков
Частичная специализация лежит в основе типовых признаков. Вот самостоятельно реализованный вариант remove_pointer.
#include <iostream>
#include <type_traits>
template <typename T>
struct RemovePtr { using type = T; };
template <typename T>
struct RemovePtr<T*> { using type = T; };
int main() {
std::cout << std::boolalpha;
std::cout << std::is_same<RemovePtr<int*>::type, int>::value << '\n';
return 0;
}Объединение шаблонов
Шаблоны можно комбинировать, например сопоставлять указатель на константный объект для обработки многоуровневых структур типов.
#include <iostream>
template <typename T>
struct Desc { static const char* k() { return "plain"; } };
template <typename T>
struct Desc<const T*> { static const char* k() { return "ptr to const"; } };
int main() {
std::cout << Desc<int>::k() << '\n';
std::cout << Desc<const int*>::k() << '\n';
return 0;
}Практический типовой признак контейнера
Используйте частичную специализацию, чтобы извлечь тип элемента из шаблона, похожего на контейнер.
#include <iostream>
#include <type_traits>
template <typename C>
struct Element { using type = void; };
template <template <typename> class C, typename T>
struct Element<C<T>> { using type = T; };
template <typename T>
struct Box { T item; };
int main() {
std::cout << std::boolalpha;
std::cout << std::is_same<Element<Box<int>>::type, int>::value << '\n';
return 0;
}Быстрая проверка
Проверьте, насколько хорошо Вы понимаете частичную специализацию.
Итоги
Вы узнали о частичной специализации:
- она настраивает шаблон класса для шаблона типов, например
T*илиconst T - для функций используется перегрузка, а не частичная специализация
- выбирается наиболее специализированный подходящий вариант
- она лежит в основе типовых признаков, таких как
remove_pointer
Далее Вы изучите SFINAE — способ включать функции только тогда, когда тип соответствует требованиям.
Часто задаваемые вопросы
Урок «Частичная специализация» бесплатный?
Да — полный текст урока «Частичная специализация» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Частичная специализация»?
Специализируйте шаблоны Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Частичная специализация»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Полная специализация
- Частичная специализация
- SFINAE
- Шаблоны enable_if