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

  Юрий  | 

  |

  Обновл. 15 Сен 2021  | 

 24030

 ǀ   3 

На уроке о специализации шаблона функции мы рассматривали шаблон класса Repository:

Мы говорили о проблеме этого шаблона при работе с типом char*, когда выполнялось поверхностное копирование (присваивание указателя) в конструкторе класса Repository. В качестве решения мы использовали полную специализацию шаблона для создания специализированной версии конструктора класса Repository для работы с типом char*, в котором выделялась память и выполнялось глубокое копирование m_value. Вот специализация конструктора и деструктора класса Repository для работы с типом char* (из материалов того же урока):

Хотя всё отлично работает с типом char*, но как насчет других типов указателей (например, int*)? Поскольку T — это любой тип указателя, то при работе с тем же int* выполнится поверхностное копирование (что нам не нужно), либо нам придется дублировать вышеприведенный код (специализация конструктора и деструктора), но уже вместо char* использовать int*. А дублирование кода, как мы уже знаем, не самый лучший вариант!

К счастью, используя частичную специализацию шаблона, мы можем определить специальную версию класса Repository, которая работала бы со всеми типами указателей (при этом не нужно указывать конкретные типы указателей):

И пример из практики:

Результат:

6
8

При объявлении объекта myintptr с типом int*, компилятор видит, что мы ранее определили частичную специализацию шаблона класса для работы с типами указателей, и, учитывая, что мы использовали тип int*, компилятор создаст экземпляр частичной специализации шаблона для работы с типом указателя. Конструктор этой специализации выполняет глубокое копирование параметра x. Позже, когда мы изменяем значение x на 10, myintptr.m_value никак не задевается, так как выполнилось глубокое копирование, при котором m_value получил свою собственную копию x.

Если бы этой частичной специализации не существовало, то создался бы экземпляр общего шаблона класса, в котором выполнилось бы поверхностное копирование, а myintptr.m_value и x указывали бы на один и тот же адрес памяти. В таком случае, при изменении значения переменной x на 10, мы также затронули бы и значение myintptr (оно также стало бы равно 10).

Стоит отметить, что, поскольку в нашей частичной специализации копируется только одно значение, при работе со строками C-style копироваться будет только первый символ (так как строка — это массив, а указатель на массив указывает только на первый элемент массива). Если же нужно полностью скопировать строку, то специализация конструктора (и деструктора) для типа char* должна быть полной. В таком случае, полная специализация будет иметь приоритет выше, чем частичная специализация. Например, вот программа, в которой используется как частичная специализация для работы с типами указателей, так и полная специализация для работы с типом char*:

Всё работает как нужно:

6
8
Anton

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

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

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

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

  1. kmish:

    Таки получилось сделать Родительский шаблон! Убрал специализацию для char для краткости. Данная реализация предотвращает дублирование кода:

    Результат:

    6
    8

    1. TrU3Ta1ent:

      Подскажи почему не получается инициализировать родительский конструктор через список инициализации при использовании шаблона класса?

  2. kmish:

    Но тут опять же проблема дублирующего кода возникает (у общего шаблона и специализации указателей)… И в данном случае, похоже, поделать с этим нечего. Родитеский шаблон придумать не получилось… m_value не может быть одновременно указателем и переменной.

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

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