Parameter pack
В следующую функцию можно передать ноль или более различных аргументов любого типа.
template<typename... Args>
void f(Args&&... args) {
// ...
}
Как теперь можно работать с аргументом args
?
Можно развернуть его в список аргументов другой функции, как если бы мы перечислили аргументы через запятую.
template<typename... Args>
void f(Args&&... args) {
std::cout << sum(args...);
}
Теперь если мы вызовем f(1, 1.f, '1')
, то в консоль выведется результат sum(1, 1.f, '1')
.
Parameter pack можно использовать и для более хитрых вещей, например, в следующем примере:
template<typename... Args>
void f(Args&&... args) {
std::cout << sum((args * 2)...);
}
Конструкция ((args * 2)...)
развернется в (a1 * 2, a2 * 2, a3 * 2)
.
Раскрывать parameter pack можно с помощью, например, рекурсии:
int sum(int t) {
return t;
}
template <typename... Tail>
int sum(int t, Tail... tail) {
return t + sum(tail...);
}
template<typename... Args>
void f(Args... args) {
std::cout << sum(args...);
}
f(1, 2, 3); // out: 6
С помощью variadic templates, type_traits
и SFINAE
можно делать умопомрачительные вещи (в том числе в compile-time), но мы опустим этот момент.
Fold expressions (C++17)
Синтаксис:
- ( pack_name op ... )
- ( ... op pack_name )
- ( pack_name op ... op init )
- (init op ... op pack )
где pack_name - имя parameter pack, op - оператор, init - начальное значение.
Во что эти конструкции разворачиваются:
- ( E op ... ) -> ( E1 op (... op ( En-1 op En )))
- ( ... op E) -> ((( E1 op E2 ) op ...) op En )
- ( E op ... op I ) -> ( E1 op (... op ( En op I )))
- ( I op ... op E ) -> ((( I op E1 ) op ...) op En )
Битовое И переменного числа аргументов
template <typename... Args>
bool all(Args... args) {
return (... && args);
}
static_assert(all(true, true, false) == false);
static_assert(all() == true); // ?
Для раскрытия parameter pack длины 0 некоторые операторы имеют значения по умолчанию:
- Логическое И ->
true
- Логическое ИЛИ ->
false
- Оператор запятой
,
->void()
(?)
Вывод в консоль переменного числа аргументов
template<typename... Args>
void print(Args&&... args) {
(std::cout << ... << args) << '\n';
}