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

  Юрий  | 

    | 

  Обновл. 13 Июл 2019  | 

 2689

 ǀ   2 

В уроке о специализации шаблона функции, мы рассматривали шаблон класса 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 копироваться будет только первый символ (так как строка это массив, а указатель на массив указывает только на первый элемент массива — детальнее об этом в уроке №82). Если же нужно скопировать целую строку, то специализация конструктора (и деструктора) для типа char* должна быть полной. В таком случае, полная специализация будет иметь приоритет выше, чем частичная специализация. Например, вот программа, в которой используется как частичная специализация для работы с типами указателей, так и полная специализация для работы с типом char*:

Всё работает как часики:

6
8
Anton

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

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

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

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

  1. Аватар kmish:

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

    Результат:

    6
    8

  2. Аватар kmish:

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

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

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