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));
В этом развертывании осуществляется попытка вызова функции debug_rep() со списком из пяти аргументов. Нет никакой версии функции debug_rep(), соответствующей этому вызову. Функция debug_rep() имеет постоянное количество аргументов, и нет никакой ее версии с пятью параметрами.
Схема при развертывании применяется по отдельности к каждому элементу в пакете.
Упражнения раздела 16.4.2
Упражнение 16.56. Напишите и проверьте версию функции errorMsg() с переменным количеством аргументов.
Упражнение 16.57. Сравните свою версию функции errorMsg() с переменным количеством аргументов с функцией error_msg() из раздела 6.2.6. Каковы преимущества и недостатки каждого подхода?
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК