Шаблоны функций. Параметры по умолчанию. С++11

Язык C++ со временем эволюционирует. Разработчики языка дают всё новые и новые возможности. Со времён первых компиляторов C++ произошло очень много изменений, появились относительно новые возможности. Одной из таких новых возможностей является использование шаблонных параметров по умолчанию в применении шаблонности к обычным функциям.
На заметку:

  • На использование шаблонных параметров по умолчанию до принятия стандарта C++11 накладывались некоторые ограничения, одним из накладываемых ограничений было использование шаблонных параметров по умолчанию только внутри классов, для функций-членов классов. После принятия стандарта C++11 стало доступным использовать такую языковую возможность не только при описании внутренностей классов, но и для обычных функций.
Использование шаблонных параметров по умолчанию при написании классов и использование шаблонных параметров по умолчанию при написании обычных функций отличается кое-чем, поэтому, чтобы вы не запутались и не смешали всё воедино, я оговариваю уже сейчас: "В этой статье описывается использование шаблонных параметров по умолчанию именно для обычных функций".
На самом деле эта тема чуть сложнее, чем многие предыдущие мои темы этого сайта, и требует от меня большей ответственности, поэтому прошу быть терпеливыми и с пониманием отнестись к получаемому многословию.
  • В шаблоны, как и в функции, уходят некоторые данные. Если функции питаются значениями, то шаблоны, в основном, питаются типами входящих в функции значений, хотя и значения они глотать умеют.
На словах это мне очень сложно истолковать, гораздо быстрее придёт понимание после разбора вами простейшего примера:
ВНИМАНИЕ!

  • Если ваш компилятор не поддерживает C++11 или поддерживает, но поддержка выключена настройками, то показываемые в статье примеры работать у вас не будут.

В этом примере задействована шаблонная функция foo, в первый раз для которой аргументы шаблону явно не задаются, срабатывает значение, описанное в шаблоне, а во второй раз аргумент шаблону задаётся явно, в угловых скобках, заданное значение приходит в параметр шаблона и шаблон использует задействует свой параметр, с пришедшим в него значением, внутри своей функции.
Иногда это может быть удобно. Кроме того, параметрам, описываемым в шаблоне, можно задавать не только значения, не являющиеся типами, но и типы как значения. Всё-таки шаблоны — это изначально рутина с типами.
Код, который будет сейчас показан, не несёт в себе практической ценности, он выбран только для лёгкости понимания основного момента: шаблонные параметры по умолчанию. Сама идея ветвления внутри шаблона в зависимости от типа — порочна. Ни к чему хорошему подобный подход вас не приведёт.

Мы три раза задействуем шаблонную функцию, подсовывая в шаблон различные аргументы: в первый раз аргументы вообще не подсовавали, второй раз подсунули один аргумент, обычное значение 100, и в третий раз подсунули два аргумента, причём первый обычное число 100, а второй тип string. Поскольку в параметрах шаблона имеют место быть параметры с заданными для по умолчанию им значениями, то в первый раз оба значения определились как значения по умолчанию: нет входящих аргументов, шаблон использует значения из себя. Во второй раз первый параметр шаблона был перебит засовываемым в шаблон аргументом, а второй ничем не перебивается, второго аргумента в шаблон мы не отдавали, поэтому только второй параметр сработал по умолчанию. В третьем случае всё очень просто, потому что просто мы явно подсунули в шаблонные параметры значения, отдав шаблону весь набор аргументов, задействовав угловые скобки в месте вызова функции. Шаблонные параметры, задаваемые для случая по умолчанию, в таком случае перебиваются значениями входящих в шаблон аргументов.
На игрушечном примере не объяснить настоящую пользу от подобного умения, но позднее вы сможете встретиться с ситуацией, когда этот приём сможет оказаться очень даже уместен.
Вообще, шаблонные параметры по умолчанию очень напоминают параметры функций по умолчанию. По сути — они одно и то же, но параметры шаблонов по умолчанию имеют некоторые отличия, связанные с природой шаблонов. В функциях есть строгое правило: указывать параметры по умолчанию в конце списка формальных параметров, а в шаблоне параметры по умолчанию можно задавать в любом месте, но при этом должно соблюдаться условие, что все параметры шаблона должны уметь или выводиться сами, или знать, чем они должны быть. Т. е. если параметр шаблона не может знать, чем он должен стать, то мы обязаны ему подсказать это явно в момент обращения к нему. Так, ранее мы использовали угловые скобки со значениями 200 и 300, подсказывая шаблону, чем должен стать его параметр.
Очередная ситуация, когда код красноречивее моих слов:

В показанном коде для шаблонного параметра x задано значение по умолчанию, поэтому даже если мы не подпитываем шаблон смыслом для существования x, шаблон сам говорит, как должно жить это x, а вот для T смысл не подсказали ни мы, ни шаблон, поэтому закомментированная строчка (отмеченная как ошибка) не может скомпилироваться. Но в том случае, если мы даём узнать смысл всем параметрам шаблона, то тогда всё проходит на ура, несмотря на то, что шаблонный параметр по умолчанию стоит левее обычного шаблонного параметра.
Как бы там ни было, использовать подобный подход неразумно. Если ставить параметры по умолчанию левее обычных параметров, то теряется смысл параметров по умолчанию: приходится явно задавать им значения, из-за этого вся фишка умолчания сводится на нет. Поэтому как и в случае с обычными функциями, лучше шаблонные параметры по умолчанию задействовать в конце списка параметров. Тогда при вызове шаблонной функции можно будет опускать явное указание значений, когда это будет нужно.
Материал, описываемый ниже, требует дополнительной осведомлённости. Честно признаюсь, что я полной осведомлённостью не обладаю. Мне ещё учиться и учиться, но пример, который покажу, считаю, должен быть здесь показан.

Шаблон сначала принимает значения, для отдачи в шаблон и приёма шаблонными параметрами значений используют угловые скобки, а потом шаблон помогает компилятору генерировать функции с нужными типами, основываясь на вошедших в шаблонные параметры значениями. Для второго задействуются круглые скобки функции. Таким образом, явное неуказание нами аргументов шаблону спровоцировало задействование параметров по умолчанию, описанных в шаблоне.
Несмотря на то, что основная часть, написанная в main может работать, в комментариях помечено, что не всё гладко в некоторых местах. Из-за того, что мы перебиваем шаблонный параметр по умолчанию числом большим, чем первая размерность массива, в конечном счёте мы неправильно обходим массив, вылезая за его границы, из-за этого на экран выводятся мусорные значения. Но если перебивать значение меньшим, чем заданная массиву размерность значением, то тогда всё нормально, просто строки будут обработаны неполностью. Но этот пример прежде всего приведён из-за возможности задействования шаблонных параметров по умолчанию и возможностью перебития их размерностью массива. Полный процесс происходящего сейчас пока что рано объяснять (да я его и сам вообще не знаю), но этот приём работы с массивом может вам вполне пригодиться.
Закомментированная строчка, помеченная как ошибка, ошибочна, потому что от нас происходит попытка подсунуть массив (*arr)[3] в массив (*arr)[2]. Работа с шаблонами требует хорошего понимания типов, а чистые массивы с разными размерностями считаются разными типами, происходит несоответствие типов и компилятор сбивается.
Глубокое изучение языка С++ даст вам поннять, почему всё это происходит с массивом, но сейчас пока, очень возможно, нужно ещё многое изучить. Тем не менее, вы поняв показанный пример с двумерным массивом, уже сейчас сможете использовать такой трюк при написании вами ваших текущих программ.

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

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

Поиск

 
     

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

https://www.litres.ru/valeriy-laptev/c-ekspress-kurs/?lfrom=15589587
Яндекс.Метрика