Урок №179. Частичная специализация шаблона

  Юрий Ворон  | 

    | 

  Обновлено 25 Ноя 2018  | 

 439

 ǀ   2 

В уроке о параметре non-type шаблона мы узнали, каким образом можно использовать дополнительный параметр шаблона.

Рассмотрим еще раз класс StaticArray из того же урока:

Здесь у нас 2 параметра шаблона класса: параметр типа и параметр non-type.

Теперь предположим, что нам нужно написать функцию для вывода всех элементов массива. Хотя мы можем сделать это через метод класса, мы реализуем это через отдельную функцию (ради лучшего погружения в тему).

Используя шаблон функции, мы можем написать следующее:

Это позволит нам сделать:

И получить:



0 1 2 3 4

Хотя всё работает правильно, но есть нюанс. Рассмотрим следующий код функции main():

(Мы рассматривали strcpy_s в уроке о строках C-style)

Программа скомпилируется, выполнится и выдаст следующий результат:

H e l l o ,   w o r l d !

Для типов не char имеет смысл помещать пробел между каждым элементом массива, чтобы элементы не «слипались». Однако с типом char есть смысл вывести всё вместе, как строку C-style, чтобы не было лишних пробелов.

Как мы можем это исправить?

Полная специализация шаблона — решение?

Сначала мы могли бы подумать об использовании специализации шаблона функции. Однако, проблема с полной специализацией шаблона заключается в том, что все параметры шаблона должны быть явно определены.

Например:

Как вы видете, мы добавили шаблон функции print для работы с типом char. Результат:

Hello, world!

Хотя одна проблема решена, возникает другая проблема: использование полной специализации шаблона класса означает, что мы должны явно указывать длину передаваемого массива! Рассмотрим следующий пример:

Вызов print(char12) вызовет шаблон функции print() с общим шаблоном StaticArray<T, size>, так как char12 является типа StaticArray<char, 12>, а шаблон функции print() принимает только StaticArray<char, 14> (длина массива отличается).

Хотя мы могли бы скопировать еще раз шаблон функции print() для работы с StaticArray<char, 12>, но это не эффективно. А что, если нам нужно будет позднее использовать массив с 5 элементов или 20? Опять копировать шаблон? Это лишняя работа.

Очевидно, полная специализация шаблона класса здесь является решением-костылем. Частичная специализация шаблона – вот, что нам нужно.



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

Частичная специализация шаблона позволяет выполнить специализацию шаблона класса (но не функции!), где некоторые, но не все, параметры шаблона явно определены. Для нашей задачи выше идеальное решение заключается в том, чтобы шаблон функции print работал со StaticArray типа char, но при этом размер массива не являлся фиксированным значением, а мог варьироваться.

Вот наш шаблон функции print, который принимает частично специализированный шаблон класса StaticArray:

Как вы можете видеть, мы здесь явно указали тип char, но size оставили не фиксированным, поэтому функция print() будет работать с массивами типа char любого размера. Вот и всё!

Полная программа:

Результат:

Hello, world! Hello, dad!

Как и ожидалось.

Обратите внимание, начиная с C++14 частичная специализация шаблона может использоваться только с классами, но не с отдельными функциями (для функций только полная специализация шаблона). Наш пример void print(StaticArray<char, size> & array) работает только потому, что шаблон функции print принимает в качестве параметра шаблон класса, который, в свою очередь, частично специализирован.

Частичная специализация шаблонов методов

Ограничение частичной специализации на функции может привести к некоторым проблемам при работе с методами класса. Например, что, если бы мы определили StaticArray следующим образом?

print() является методом класса StaticArray<T, int>. Что произойдет, если мы захотим частично специализировать шаблон функции print(), чтобы метод работал по-другому? Мы можем попробовать сделать следующее:

К сожалению, это не сработает, так как мы пытаемся частично специализировать шаблон функции, что делать запрещается.

Как же это можно обойти? Одним из очевидных решений является частичная специализация шаблона всего класса:

Результат:

0 1 2 3 4
4.000000e+00 4.100000e+00 4.200000e+00 4.300000e+00

Хотя это работает, но это не самый лучший вариант, так как у нас теперь куча дублированного кода из StaticArray<T, size> в StaticArray<double, size>.

Если бы можно было бы использовать код из StaticArray<T, size> в StaticArray<double, size> без дублирования. Не напоминает вам что-то? Как по мне, то это звучит, как работа с наследованием!

Вы можете начать с:

Но как мы можем ссылаться на StaticArray? Мы не можем.

К счастью, есть обходной путь с использованием общего родительского класса:

Результат тот же, что и выше, но дублированного кода меньше.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (4 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Doriz:

    Почему специализация

    является полной, а не частичной?

    1. Юрий Ворон Юрий Ворон:

      Это не специализация, это шаблон функции print() с одним параметром non-type. Спасибо, исправил.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *