0Pricing
C++ Academy · Урок

Частичная специализация

Специализируйте шаблоны

«Частичная специализация» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Полная специализация
  2. Частичная специализация
  3. SFINAE
  4. Шаблоны enable_if
← Назад к C++ Academy