template
auto sum(Ts ts)
{
return (ts + ...);
}
3. Теперь можно вызвать функцию следующим образом:
int the_sum {sum(1, 2, 3, 4, 5)}; // Значение: 15
4. Она работает не только с целочисленными типами; можно вызвать ее для любого типа, реализующего оператор +
, например std::string
:
std::string a {"Hello "};
std::string b {"World"};
std::cout << sum(a, b) << '\n'; // Вывод: Hello World
Как это работает
Только что мы написали код, в котором с помощью простой рекурсии бинарный оператор (+
) применяется к заданным параметрам. Как правило, это называется
Подобное выражение называется +
, –
, *
, /
, %
, ^
, &
, |
,
=
, <
, >
, <<
, >>
, +=
, –=
, *=
, /=
, %=
, ^=
, &=
, |=
, <<=
, >>=
, ==
, !=
, <=
, >=
, &&
, ||
, ,
, .*
, –>*
.
Кстати, в нашем примере кода неважно, какую использовать конструкцию, (ts +
…) или (… + ts
);. Они обе работают так, как нужно. Однако между ними есть разница, которая может иметь значение в других случаях: если многоточие …
находится с
В нашем примере с суммой левая унарная свертка разворачивается в конструкцию 1+(2+(3+(4+5)))
, а правая унарная свертка развернется в (((1+2)+3)+4)+5
. В зависимости от того, какой оператор используется, могут проявиться нюансы. При добавлении новых чисел ничего не меняется.
Дополнительная информация
Если кто-то вызовет функцию sum()
и 0
.
Это делается так:
template
auto sum(Ts ... ts)
{
return (ts + ... + 0);
}
Таким образом, вызов sum()
возвращает значение 0
, а вызов sum(1, 2, 3)
— значение (1+(2+(3+0)))
. Подобные свертки с начальным значением называются
Кроме того, обе конструкции, (ts + ... + 0)
и (0 + ... + ts)
, работают как полагается, но такая бинарная свертка становится
При использовании бинарных сверток для решения такой задачи, когда аргументы отсутствуют, очень важны 0
нейтральным элементом. Поэтому можно добавить 0
к любому выражению свертки с помощью операторов +
или –
. Если пакет параметров пуст, это приведет к возврату функцией значения 0
. С математической точки зрения это правильно. С точки зрения реализации нужно определить, что именно является правильным в зависимости от наших требований.
Тот же принцип применяется и к умножению. Здесь нейтральным элементом станет 1
:
template
auto product(Ts ts)
{
return (ts * ... * 1);
}
Результат вызова product(2, 3)
равен 6
, а результат вызова product()
без параметров равен 1
.
В логических операторах И (&&)
и ИЛИ (||)
появились &&
заменяется на true
, а свертка пустого пакета с оператором ||
— на false
.
Еще один оператор, для которого определено значение по умолчанию, когда он используется для пустых пакетов параметров, — это оператор «запятая» (,
), заменяемый на void()
.
Давайте взглянем на другие вспомогательные функции, которые можно реализовать с помощью этих механизмов.
Соотнесение диапазонов и отдельных элементов
Как насчет функции, которая определяет, содержит ли диапазон
template
auto matches(const R& range, Ts ... ts)
{
return (std::count(std::begin(range), std::end(range), ts) + ...);
}