5.3. Настройка сигнатуры для передачи данных
В рассмотренной выше реализации распределения с передачей данных (п. 5.2.2) есть один недостаток: данные, передаваемые в вызов, имеют заранее прописанную сигнатуру. В нашем случае предполагается, что это единственная числовая переменная. Если нам понадобится другая сигнатура, т. е. другой набор и типы переменных, нам придется повторять всю реализацию распределения, изменяя только сам вызов. Можно ли настроить сигнатуру, как это мы делали в универсальном аргументе? Тогда мы определяли сигнатуру с помощью пакета параметров, но теперь у нас пакет параметров используется для задания объектов вызова.
Получается, нам необходим еще один пакет параметров. В общем случае допускается объявлять шаблон функции с несколькими пакетами 30 30 Но не шаблон класса, в шаблонах классов пакет параметров может быть только один. Кроме того, если в шаблоне объявляется пакет параметров, он должен быть последним в списке параметров шаблона.
, однако в этом случае для вывода типов пакета используется схема раскрытия. По этой причине необходимо, чтобы все пакеты параметров раскрывались параллельно в рамках одной синтаксической конструкции (Листинг 67), что для нашей задачи не подходит: мы должны вначале раскрыть пакет объектов вызова, а затем для каждого элемента пакета раскрыть пакет сигнатуры. Здесь нужно какое-то другое решение.
Листинг 67. Пример шаблона функции с несколькими пакетами параметров
template
void init(std::pair…)
{
}
int main()
{
init(std::make_pair(1, 2), std::make_pair(3,4), std::make_pair(0.3, 1e5));
}
Поскольку пакет параметров в нашем случае может быть только один, необходима структура данных, в которую можно упаковать объекты различных типов. На эту роль лучше всего подойдет кортеж.
Кортеж– это структура данных, которая используется для хранения объектов различных типов.
В STL кортеж реализуется шаблонным классом std::tuple, параметрами шаблона являются типы, которые будут храниться в кортеже. Этот класс как нельзя лучше подойдет для наших целей, потому что объекты вызова у нас также задаются параметрами шаблона.
Итак, у нас есть два набора: объекты вызова и данные, передаваемые в вызов. Какой набор упаковать в кортеж, а какой в пакет параметров? Рассмотрим различные способы упаковки наборов.
5.3.2. Способ 1: объекты в пакет, данные в кортеж
При использовании данного способа реализация распределения практически совпадает с описанной в Листинг 65 п. 5.2.2 с той разницей, что для передачи данных используется не переменная, а кортеж (Листинг 68).
Листинг 68. Распределение при упаковке объектов в пакет и данных в кортеж
template
void Call(CallData& data) // (1)
{
}
template
void Call(CallData& data, First& first, Others&…rest) // (2)
{
std::apply(first, data); // (3)
Call(data, rest…); // (4)
}
template
void Distribute1(std::tuple data, CallObjects… objects) // (5)
{
Call(data, objects…); // (6)
}
Распределяющая функция объявлена в строке 5. Входными параметрами функции являются кортеж данных вызова dataи пакет объектов вызова objects, типы их содержимого задаются параметрами шаблона. Внутри этой функции, в строке 6, происходит первый вызов рекурсивной функции, которой передаются соответствующие аргументы – кортеж и пакет.
Рекурсивная функция объявлена в строке 2. Эта функция извлекает очередной объект из пакета и осуществляет его вызов (строка 3). Здесь используется функция стандартной библиотеки std::apply, которая преобразует содержимое кортежа в список аргументов. Далее, в строке 4, пакет с оставшимися параметрами передается в рекурсивный вызов Call, и процесс повторяется до завершения рекурсии.
5.3.3. Способ 2: объекты в кортеж, данные в пакет
При использовании данного способа необходимо пройти по всем элементам кортежа и осуществить вызовы хранимых в нем объектов, передавая на вход пакет данных. Как осуществить обход содержимого кортежа?
Доступ к элементам кортежа осуществляется с помощью вызова
std::get(tuple),
где index– это порядковый номер элемента (начиная с 0), tuple– имя переменной-кортежа. Проблема в том, что индексы должны быть заранее определены как числовые константы, использование переменной для задания индекса не допускается 31 31 Это связано с тем, что функция получения элемента кортежа по индексу объявлена как шаблон с параметром – числовым значением. Переменные не могут выступать параметрами шаблона.
. Поэтому здесь нельзя использовать ни циклы, ни функции с входным аргументом – индексом.
Читать дальше