- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Функции с переменным количеством аргументов зачастую рекурсивны (см. раздел 6.3.2). Первый вызов обрабатывает первый аргумент в пакете и вызывает себя для остальных аргументов. Новая функция print() будет работать таким же образом — каждый вызов выводит свой второй аргумент в поток, обозначенный первым аргументом. Для остановки рекурсии следует определить также обычную функцию print(), которая получает поток и объект:
// Функция для завершения рекурсии и вывода последнего элемента
// ее следует объявить перед определением версией print() с переменным
// количеством аргументов
template<typename Т>
ostream &print(ostream &os, const T &t) {
return os << t; // нет разделителя после последнего элемента в пакете
}
// эта версия print() будет вызвана для всех элементов в пакете, кроме
// последнего
template <typename Т, typename... Args>
ostream &print(ostream &os, const T &t, const Args&... rest) {
os << t << ", "; // выводит первый аргумент
return print(os, rest...); // рекурсивный вызов; вывод других
// аргументов
}
Первая версия функции print() останавливает рекурсию и выводит последний аргумент в начальном вызове функции print(). Вторая версия, с переменным количеством аргументов, выводит аргумент, связанный с t, и вызывает себя для вывода остальных значений в пакете параметров функции.
Ключевая часть — вызов функции print() в функции с переменным количеством аргументов:
return print(os, rest...); // рекурсивный вызов; вывод других
// аргументов
Версия функции print() с переменным количеством аргументов получает три параметра: ostream&, const T& и пакет параметров. Но в этом вызове передаются только два аргумента. В результате первый аргумент в пакете rest привязывается к t. Остальные аргументы в пакете rest формируют пакет параметров для следующего вызова функции print(). Таким образом, при каждом вызове первый аргумент удаляется из пакета и становится аргументом, связанным с t. Соответственно, получаем:
print(cout, i, s, 42); // два параметра в пакете
Рекурсия выполнится следующим образом:
Вызов t rest... print(cout, i, s, 42) i s, 42 print(cout, s, 42) s 42Вызов print(cout, 42) вызывает обычную версию функции print().
Первые два вызова могут соответствовать только версии функции print() с переменным количеством аргументов, поскольку обычная версия не является подходящей. Эти вызовы передают четыре и три аргумента соответственно, а обычная функция print() получает только два аргумента.
Для последнего вызова в рекурсии, print(cout, 42), подходят обе версии функции print(). Этот вызов передает два аргумента, и типом первого являются ostream&. Таким образом, подходящей является обычная версия функции print().
Версия с переменным количеством аргументов также является подходящей. В отличие от обычного аргумента, пакет параметров может быть пустым. Следовательно, экземпляр версии функции print() с переменным количеством аргументов может быть создан только с двумя параметрами: один — для параметра ostream& и другой — для параметра const T&.
Обе функции обеспечивают одинаково хорошее соответствие для вызова. Однако нешаблонная версия с переменным количеством аргументов более специализирована, чем шаблонная с переменным количеством аргументов. Поэтому выбирается версия без переменного количества аргументов (см. раздел 16.3).
Объявление версии функции print() с постоянным количеством аргументов должно быть в области видимости, когда определяется версия с переменным количеством аргументов. В противном случае функция с переменным количеством аргументов будет рекурсивно вызывать себя бесконечно.
Упражнения раздела 16.4.1Упражнение 16.53. Напишите собственные версии функций print() и проверьте их, выводя один, два и пять аргументов, у каждых из которых должны быть разные типы.
Упражнение 16.54. Что происходит при вызове функции print() для типа, не имеющего оператора <<?
Упражнение 16.55. Объясните, как выполнилась бы версия функции print() с переменным количеством аргументов, если бы обычная версия функции print() была объявлена после определения версии с переменным количеством аргументов.
16.4.2. Развертывание пакета
Кроме выяснения размера, единственное, что можно еще сделать с пакетом параметров, — это развернуть (pack expansion) его. При развертывании пакета предоставляется схема (pattern), используемая для каждого развернутого элемента. Развертывание пакета разделяет его на элементы с применением схемы к каждому из них. Для запуска развертывания справа от схемы помещают многоточие (...).
Например, функция print() содержит два развертывания:
template <typename Т, typename... Args> ostream &
print(ostream &os, const T &t, const Args&... rest) // развертывание
// Args
{
os << t << ", ";
return print(os, rest...); // развертывание rest
}
В первом случае развертывание пакета параметров шаблона создает список параметров функции print(). Второй случай развертывания находится в вызове функции print(). Эта схема создает список аргументов для вызова.
Развертывание пакета Args применяет схему const Args& к каждому элементу в пакете параметров шаблона Args. Результатом этой схемы будет разделенный запятыми список из любого количества типов параметров в формате const тип&. Например:
print(cout, i, s, 42); // два параметра в пакете
Типы последних двух аргументов, наряду со схемой, определяют типы замыкающих параметров. Этот вызов создает следующий экземпляр:
ostream&
print(ostream&, const int&, const strings, const int&);
Второе развертывание происходит в рекурсивном вызове функции print(). В данном случае схема — это имя пакета параметров функции (т.е. rest). Эта схема развертывается в разделяемый запятыми список элементов пакета. Таким образом, этот вызов эквивалентен следующему:
print(os, s, 42);
Концепция развертывания пакетаРазвертывание пакета параметров функции print() только разворачивало пакет на его составные части. При развертывании пакета параметров функции возможны и более сложные схемы. Например, можно было бы написать вторую функцию с переменным количеством аргументов, которая вызывает функцию debug_rep() (см. раздел 16.3) для каждого из своих аргументов, а затем вызывает функцию print(), чтобы вывести полученные строки:
// вызвать debug_rep() для каждого аргумента в вызове print()
template <typename... Args>
ostream &errorMsg(ostream &os, const Args&... rest) {
// print(os, debug_rep(a1), debug_rep(a2), ..., debug_rep(an)
return print(os, debug_rep(rest)...);
}
Вызов функции print() использует схему debug_rep(rest). Эта схема означает, что функцию debug_rep() следует вызвать для каждого элемента в пакете параметров функции rest. Получившийся развернутый пакет будет разделяемым запятыми списком вызовов функции debug_rep(). Таким образом, вызов
errorMsg(cerr, fcnName, code.num(), otherData, "other", item);
выполняется, как будто было написано:
print(cerr, debug_rep(fcnName), debug_rep(code.num()),
debug_rep(otherData), debug_rep("otherData"),
debug_rep(item));
Следующая схема, напротив, не была бы откомпилирована:
// передает пакет debug_rep(); print(os, debug_rep(a1, a2, an))
print(os, debug_rep(rest...)); // ошибка: нет функции, соответствующей
// вызову
Проблема здесь в том, что пакет rest развернут в вызове функции debug_rep(). Этот вызов выполнился бы так, как будто было написано:
print(cerr, debug_rep(fcnName, code.num(),
otherData, "otherData", item));

