Шаблоны. Специализации шаблонов класса в С++.

  • Для тех, у кого Visual Studio, может пригодится вот это видео:
Иногда бывают ситуации, где имеется эквивалентная функциональность. В простейшем случае такой ситуацией может оказаться операция сложения: десятичные числа складываются своим способом, строки своим, а какие-нибудь римские цифры своим. Несмотря на то, что способы разные, мы используем одну и ту же форму записи: для сложения обычно используется +. Могут быть совершенно разные задачи, где возникнет вопрос эквивалентных реализаций, специализированных под определённые типы. Для решения этой проблемы нам дана возможность специализировать шаблоны. Это значит, что если для функций или переменных класса определено наиболее общее поведение, но в класс попадает тип, поведение которого разнится с существующим, мы должны специализировать шаблон. Можно специализировать или отдельную функцию или сам класс.
  • Возможность специализации класса существует для того, чтобы мы могли скорректировать поведение чего-либо под нужды неподходящего наиболее общему решению типа.
  • Чтобы специализировать шаблон класса, следует объявить класс, предварив его конструкцией template< >, и указать типы, для которых специализируется шаблон класса. Типы используются в качестве аргументов шаблона и задаются непосредственно после имени класса.


  • Специализированные функции перекрывают выполнение обобщённой функции, а специализированные классы перекрывают описание обобщённого класса.
Факт перекрытия увидеть несложно. Давайте напишем класс, который будет уметь менять местами объекты некоторых типов.

Объекты меняются местами. Всё хорошо. Для понимания листинга #2 вам нужно уметь писать нешаблонную функцию, посредством которой объекты будут меняться местами. И иметь понимание об обычных шаблонных функциях. T1 у меня символизирует один вид типа, а T2 второй вид типа. Поскольку в коде я использую два вида типов: int и double, которые отдаю функции, то у меня два символических обозначения.
Давайте попробуем поменять местами две С строки.

Из-за того, что нельзя массив в массив, наш алгоритм перемены местами объектов поломался. Мы можем переписать шаблонную функцию смены таким образом, чтобы менялись такие символьные массивы, но тогда нормально перестанут меняться обычные числовые типы. Это и есть возникновение ситуации необходимости специализации шаблона. Нам необходимо описать эквивалент выполнению функции, описанной внутри шаблонного класса, что как раз и есть специализация. Давайте специализируем поведение для массива символов.

Несмотря на то, что листинг #3 компилируется и программа работает, если внимательно присмотреться, то будет видно, что целочисленные переменные остались на своих стартовых позициях. И перед нами возникает делемма: используем ссылки, не можем поменять местами массивы символов, не используем ссылки, не можем поменять местами обычные численные переменные. Для начала нужно определиться, почему при наличии параметров-ссылок в функции обмена внутри нашего класса код отказывается компилироваться. Первопричина в том, что массивы указателями не являются, и когда в функцию приходит массив название пришедшего массива неявным образом приводится к указательной переменной на первый его элемент (параметр указатель, массив приводится к указателю). Любые преобразования типов не переделывают оригинальный объект, а на основе оригинального создают клон-переменную (временную переменную), но с выбранным типом. Когда у нас параметры-ссылки, то параметр-ссылка пытается связаться с созданным приведением временным указателем. Правила С++ запрещают использовать ссылки на временные объекты, можно свзывать константные ссылки, но нам константные не подходят, а обычные мы связать не имеем права.
Вариантов решения существует несколько: специализация непосредственнно под массив, специализация под указатели, где указатели будут интерпретироваться как массивы. Для начала напишем вариант, где массивы будут масивами (он полегче к пониманию):

В листинге #4 можно наблюдать, что в параметре раскрываемого класса указывается непосредственно тот тип, ради которого проводится специализация. А вот в параметрах функции ссылки на массив могут кому-то показаться причудливыми, странными, необычными. Никто не запрещает создавать ссылки на массив, а поскольку у нас прототип меняющей функции предполагает приём в параметры ссылок, то нам нужны ссылки, а коли ссылки, то просто массив не подойдёт, нужно ссылку на массив. Как можно увидеть, теперь специализированная для массива функция перекрывает наиболее обобщённый вариант реализации обмена своей реализацией. Если так случается, что эта специализированная функция наиболее подходит для обмена (а она лучше всего подходит, когда у нас меняются местами два массива ёмкостью в 255 элементов), то она и выполняется. Но есть в этом способе и неудобство: а что если у массивов разная ёмкость? Не всегда ведь массив будет вместимостью 255. И для каждой новой ёмкости может прийтись писать отдельную специализацию. Мы написали для 255, потом напишем для 100, для 22 и т. д. Это достаточно неудобно. Хотя возможны ситуации, где достаточно специализировать именно массив, всякое случается. Но для нас лучше подойдёт наиболее общая форма, где мы не будем зависеть от количества элементов, умещающихся в массив.
Изменение задачи в выбранном направлении требует пересмотрения к написанию кода. Мы можем использовать указатель как массив, но, как ранее было выяснено, не можем связать ссылку с временным объектом-указателем, получаемым как результат неявного преобразования типов. Поскольку всё упирается в ссылку, которая не хочет связываться с указателем, полученным из неявного приведения названия массива, нам необходимо избавиться от ссылочности. Нам бы могли помочь контантные ссылки, такие ссылки имеют право быть связанными со временными объектами, но мы меняем объекты, поэтому о константности речи идти не может. Ограничение ссылочности параметров, с которым мы столкнулись, можно обойти путём специализации непосредственно класса, а не отдельной его внутренней функции.Специализация класса позволит посредством объекта класса объяснять компилятору, что аргументы функции нужны или такие-то, или такие-то. Мы объясним, что если в класс на обработку уходит массив, то наша функция должна будет принимать не ссылки, а обычные параметры (но с учётом того, что массив умеет приводится у указателю, параметры будут указателями, но не ссылками на указатели). Проделать это можно вот таким, например, образом:

Специализация всего класса позволила нам избавиться от ссылочности параметров внутриклассовой функции и благодаря этому мы смогли решить задачу по написанию класса-обменщика. Теперь объекты класса-обменщика умеют менять местами и числовые типы и массивы символов и С-строки (любые строковые представления в виде массивов символов, на которые может указывать какой-нибудь указатель). Основные моменты, которые могут спровоцировать вопросы, описаны в комментариях листинга.
  • При специализации шаблона класса необходимо специализировать все его функции-члены.

Если вам хочется попробовать что-то своими силами, добавьте в листинг #5 специализацию обменщика обычного массива, эта задача очень похожа на уже решённую, но в обычном массиве нет признака конца, поэтому имеется небольшое отличие.
Буду надеяться, что эта тема помогла вам разобраться и оказалось полезной.
Статья полностью переписана 15.05.2018г.
Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

2 комментария: Шаблоны. Специализации шаблонов класса в С++.

  • Слава (мне) говорит:

    Привет, дорогой автор!

    Почему в этом  фрагменте должна быть проблема в первой строчке ?
    MyClass<const char*> X2(«Hello»,»Bye»); //<— Заработало
    std::cout<<X2.Add();
    Специализируется ведь вторая строчка?

    • admin говорит:

      Да. Специализируется ради std::cout << X2.Add();

      Конечно, проблемы быть не должно. У меня в коде переопределение X2. Сбило с толку, наверное, когда писал.
      Позже будет исправлено.

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

Ваш e-mail не будет опубликован.

+ 55 = 58

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/denis-kolisnichenko/rukovodstvo-po-komandam-i-shell-programmirovaniu-v-linux/?lfrom=15589587
Яндекс.Метрика
НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

В зоопаpке pебенок, возбужденно тыча пальцем на клетку с пpиматами, кpичит: - Мама ! Мама ! Смотpи - пpогpаммисты ! - Почему ты так pешил ? - Они как папа ! - не мытые, лохматые и мозоли на попе !!

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок