Open In App

Fold Expressions in C++ 17

Fold expressions in C++17 are a powerful feature that allows you to reduce or “fold” a parameter pack over a binary operator. They were introduced to simplify code that operates on variadic templates and make it more concise and readable.

Syntax

Fold expressions have the following syntax forms:



(pack op ...)
(... op pack)
(pack op ... op init)
(init op ... op pack)

Here, op is any binary operator, pack is an expression containing an unexpanded parameter pack, and init is an expression without an unexpanded parameter pack.

Note: The opening and closing parentheses are a required part of the fold expression.



Types of Fold Expression

Fold expressions come in four types:

1. Unary Right Fold:

(pack op ...)

2. Unary Left Fold:

(... op pack)

3. Binary Right Fold:

(pack op ... op init)

4. Binary Left Fold:

(init op ... op pack)

In a binary fold expression, both operators op must be the same.

Examples:

Let’s look at some practical examples to understand fold expressions better:

1. Unary Left Fold Example




// C++ program to illustrate unary leftfold expression
#include <iostream>
using namespace std;
  
template <typename... Args> bool all(Args... args)
{
    return (... && args);
}
  
int main()
{
    bool b = all(true, true, true, false);
    cout << "Result: " << boolalpha << b << endl;
    return 0;
}

Output

Result: false

In this example, all function checks if all the arguments are true by performing a unary right fold with the logical AND operator &&.

2. Binary Right Fold Example




#include <iostream>
  
template<typename... Args>
int sum(Args&&... args)
{
    return (args + ...); // Performs a binary right fold with addition
}
  
int main()
{
    int result = sum(1, 2, 3, 4);
    std::cout << "Result: " << result << std::endl;
    return 0;
}

Output

Result: 10

Here, the sum function calculates the sum of all its arguments using a binary left fold with the addition operator +.

3. Unary Left Fold with Empty Pack Example




#include <iostream>
  
template<typename... Args>
bool any(Args... args) {
    return (... || args);
}
  
int main() {
    bool b = any(false, false, false);
    std::cout << "Result: " << std::boolalpha << b << std::endl;
    return 0;
}

Output

Result: false

This example demonstrates a unary left fold with an empty pack. The result is false, as the logical OR operator || returns false for an empty pack.

4. Binary Left Fold with Parentheses Example




#include <iostream>
using namespace std;
  
template <typename... Args>
int multiply_and_add(Args... args)
{
    return (1 * ... * args); // Error: operator with
                             // precedence below cast
}
  
int main()
{
    int result = multiply_and_add(1, 2, 3, 4);
    cout << "Result: " << result;
    return 0;
}

In this case, we encounter an error because the binary left-fold expression lacks parentheses around the operator *. To fix this, we need to add parentheses to ensure the correct operator precedence:




#include <iostream>
  
template<typename... Args>
int multiply_and_add(Args... args)
{
    return (1 * ... * (args)); // Corrected with parentheses
}
  
int main()
{
    int result = multiply_and_add(1, 2, 3, 4);
    std::cout << "Result: " << result << std::endl;
    return 0;
}

Output

Result: 24

Ensure that you correctly parenthesize operators when necessary, especially in binary left-fold expressions.

Conclusion

Fold expressions provide a concise and expressive way to work with parameter packs in C++, reducing the need for complex recursive code. They are a valuable addition to modern C++ and can lead to more readable and maintainable code.


Article Tags :