А.6. Шаблоны с переменным числом параметров
Функции с переменным числом параметров, например printf, используются уже давно, а теперь появились и шаблоны с переменным числом параметров (variadic templates). Такие шаблоны применяются во многих местах библиотеки С++ Thread Library. Например, конструктор std::thread для запуска потока (раздел 2.1.1) — это шаблон функции с переменным числом параметров, a std::packaged_task<> (раздел 4.2.2) — шаблон класса с переменным числом параметров. С точки зрения пользователя, достаточно знать, что шаблон принимает неограниченное количество параметров, но если вы хотите написать такой шаблон или просто любопытствуете, как это работает, то детали будут небезынтересны.
При объявлении шаблонов с переменным числом параметров, по аналогии с обычными функциями, употребляется многоточие (...) в списке параметров шаблона:
template<typename ... ParameterPack>
class my_template {};
Переменное число параметров допустимо и в частичных специализациях шаблона, даже если основной шаблон содержит фиксированное число параметров. Например, основной шаблон std::packaged_task<> (раздел 4.2.1) — это простой шаблон с единственным параметром:
template<typename FunctionType>
class packaged_task;
Однако этот основной шаблон нигде не конкретизируется, а служит лишь основой для частичных специализаций:
template<typename ReturnType, typename ... Args>
class packaged_task<ReturnType(Args...)>;
Именно внутри частичной специализации и содержится реальное определение класса; в главе 4 мы видели, что для объявления задачи, которая принимает параметры типа std::string и double и возвращает результат в виде объекта std::future<int>, можно написать std::packaged_task<int(std::string, double)>.
На примере этого объявления демонстрируются два дополнительных свойства шаблонов с переменным числом параметров. Первое сравнительно простое: разрешается в одном объявлении задавать как обычные параметры шаблона (скажем ReturnType), так и переменные (Args). Второе свойство — это использование Args... в списке аргументов специализации шаблона для обозначения того, что здесь должны быть перечислены фактические типы, подставляемые вместо Args в точке конкретизации шаблона. На самом деле, поскольку это частичная специализация, то работает она, как сопоставление с образцом; типы, встречающиеся в контексте конкретизации, запоминаются как Args. Переменное множество параметров Args называется пакетом параметров (parameter pack), а конструкция Args... — расширением пакета.
Как и для обычных функций с переменным числом параметров, переменная часть может быть как пустым списком, так и содержать много элементов. Например, в конкретизации std::packaged_task<my_class()> параметром ReturnType является my_class, а пакет параметров Args пуст. С другой стороны, в конкретизации std::packaged_task<void(int, double, my_class&, std::string*)> параметр ReturnType — это void, и Args — список, состоящий из элементов int, double, my_class&, std::string*.