Fold Expressions
Abdallah Ahmed
Embedded Linux Engineer | C++ Developer | YOCTO | Bash | Python | C | RTOS | GP Sponsored by Swift Act
C++17 introduced Fold Expressions, a powerful feature that simplifies operations on parameter packs in variadic templates.
Fold expressions allow you to apply an operator to a parameter pack concisely instead of manually expanding it using recursion or initializer_list.
Before C++17, variadic templates required recursion or an initializer_list to process a parameter pack. Let's compare how this was done before and after C++17.
1-Before C++17: Recursive Variadic Template Expansion
#include <iostream>
// Base case (when only one argument remains)
template <typename T>
T sum(T num) {
return num;
}
// Recursive case (expands parameter pack one by one)
template <typename T, typename... Args>
T sum(T first, Args... rest) {
return first + sum(rest...);
}
int main() {
std::cout << sum(1, 2, 3, 4, 5);
}
How it expands ?
For sum(1, 2, 3, 4, 5), the compiler expands it like this:
sum(1, 2, 3, 4, 5)
=> 1 + sum(2, 3, 4, 5)
=> 1 + (2 + sum(3, 4, 5))
=> 1 + (2 + (3 + sum(4, 5)))
=> 1 + (2 + (3 + (4 + sum(5))))
=> 1 + (2 + (3 + (4 + 5)))
=> 15
This works but involves multiple function calls, which can increase compile time and make debugging harder.
2-C++17: Fold Expressions (Simplified Expansion)
With C++17, fold expressions make this much simpler:
#include <iostream>
template <typename... Args>
auto sum(Args... args) {
return (... + args);
}
int main() {
std::cout << sum(1, 2, 3, 4, 5);
}
How it expands ?
The fold expression (... + args) expands directly as:
(((1 + 2) + 3) + 4) + 5
See the difference in https://cppinsights.io/
You can see the number of functions instantiated and the size for the following cases:
Types of Fold Expressions
There are four types of fold expressions in C++:
1. Unary Left Fold (( ... op pack ))
#include <iostream>
template <typename... Args>
auto sum(Args... args) {
return (... + args); // Unary left fold
}
int main() {
std::cout << sum(1, 2, 3, 4) << "\n"; // ((1 + 2) + 3) + 4 = 10
}
2. Unary Right Fold (( pack op ... ))
#include <iostream>
template <typename... Args>
auto subtract(Args... args) {
return (args - ...); // Unary right fold
}
int main() {
std::cout << subtract(10, 2, 3) << "\n"; // 10 - (2 - 3) = 10 - 2 + 3 = 11
}
3. Binary Left Fold (( init op ... op pack ))
#include <iostream>
template <typename... Args>
auto multiply(int init, Args... args) {
return (init * ... * args); // }
int main() {
std::cout << multiply(2, 3, 4) << "\n"; // (2 * 3) * 4 = 24
}
4. Binary Right Fold (( pack op ... op init ))
#include <iostream>
template <typename... Args>
auto divide(int init, Args... args) {
return (args / ... / init); // Binary right fold
}
int main() {
std::cout << divide(2, 8, 4) << "\n"; // 8 / (4 / 2) = 8 / 2 = 4
}