Выражение свёртки(начиная с C++17)
Материал из cppreference.com
Уменьшает (свёртывает) набор параметров с помощью бинарного оператора.
Синтаксис
( пакет op ... )
|
(1) | ||||||||
( ... op пакет )
|
(2) | ||||||||
( пакет op ... op init )
|
(3) | ||||||||
( init op ... op пакет )
|
(4) | ||||||||
1) унарная правосторонняя свёртка
2) унарная левосторонняя свёртка
3) бинарная правосторонняя свёртка
4) бинарная левосторонняя свёртка
| op | — | любой из следующих 32 бинарных операторов: + - * / % ^ & | = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*. В бинарной свертке оба op должны быть одинаковыми.
|
| пакет | — | выражение, которое содержит нераскрытый набор параметров и не содержит оператора с приоритетом ниже приведения на верхнем уровне (формально, выражение приведения) |
| init | — | выражение, которое не содержит нераскрытого набора параметров и не содержит оператора с приоритетом ниже приведения на верхнем уровне (формально, выражение приведения) |
Важно отметить, что открывающая и закрывающая скобки также являются частью выражения-свёртки.
Объяснение
Инициализация выражения свёртки раскрывает выражение e по следующему принципу:
1) Унарная правосторонняя свёртка
(E op ...) становится (E1 op (... op (EN-1 op EN)))2) Унарная левосторонняя свёртка
(... op E) становится (((E1 op E2) op ...) op EN)3) Бинарная правосторонняя свёртка
(E op ... op I) становится (E1 op (... op (EN−1 op (EN op I))))4) Бинарная левосторонняя свёртка
(I op ... op E) становится ((((I op E1) op E2) op ...) op EN)(где N это число элементов в раскрытом пакете)
Например,
template<typename... Args>
bool all(Args... args) { return (... && args); }
bool b = all(true, true, true, false);
// внутри all(), унарная левосторонняя свёртка расширяется как
// return ((true && true) && true) && false;
// b равно false
Когда унарная свертка используется с раскрытым пакетом нулевой длины, разрешены только следующие операторы:
1) Логическое И (
&&). Значение пустого пакета равно true2) Логическое ИЛИ (
||). Значение пустого пакета равно false3) Оператор запятая (
,). Значение пустого пакета равно void()Примечание
Если выражение, используемое как init или как пакет, имеет оператор с приоритетом ниже приведения на верхнем уровне, оно должно быть заключено в скобки:
template<typename ...Args>
int sum(Args&&... args)
{
// return (args + ... + 1 * 2); // Ошибка: оператор с приоритетом ниже приведения
return (args + ... + (1 * 2)); // OK
}
| Макрос Тестирования функциональности | Значение | Стандарт | Функциональность |
|---|---|---|---|
__cpp_fold_expressions |
201603L |
(C++17) |
Пример
Запустить этот код
#include <climits>
#include <concepts>
#include <cstdint>
#include <iostream>
#include <type_traits>
#include <utility>
#include <vector>
template<typename ...Args>
void printer(Args&&... args)
{
(std::cout << ... << args) << '\n';
}
template<typename T, typename... Args>
void push_back_vec(std::vector<T>& v, Args&&... args)
{
static_assert((std::is_constructible_v<T, Args&&> && ...));
(v.push_back(std::forward<Args>(args)), ...);
}
template<class T, std::size_t... dummy_pack>
constexpr T bswap_impl(T i, std::index_sequence<dummy_pack...>)
{
T low_byte_mask = (unsigned char)-1;
T ret{};
([&]
{
(void)dummy_pack;
ret <<= CHAR_BIT;
ret |= i & low_byte_mask;
i >>= CHAR_BIT;
}(), ...);
return ret;
}
constexpr auto bswap(std::unsigned_integral auto i)
{
return bswap_impl(i, std::make_index_sequence<sizeof(i)>{});
}
int main()
{
printer(1, 2, 3, "abc");
std::vector<int> v;
push_back_vec(v, 6, 2, 45, 12);
push_back_vec(v, 1, 2, 9);
for (int i : v) std::cout << i << ' ';
static_assert(bswap<std::uint16_t>(0x1234u)==0x3412u);
static_assert(bswap<std::uint64_t>(0x0123456789abcdefULL)==0xefcdab8967452301ULL);
}
Вывод:
123abc
6 2 45 12 1 2 9
Ссылки
- C++23 стандарт (ISO/IEC 14882:2023):
- 7.5.6 Выражения свёртки [expr.prim.fold]
- C++20 стандарт (ISO/IEC 14882:2020):
- 7.5.6 Выражения свёртки [expr.prim.fold]
- C++17 стандарт (ISO/IEC 14882:2017):
- 8.1.6 Выражения свёртки [expr.prim.fold]