CodeNebula
C++17coreadvanced

Fold Expressions

C++17 · Practical Modern · Compiler flag: -std=c++17

Explanation

Provides a concise syntax to apply a binary operator across all elements of a parameter pack, eliminating recursive template instantiations for common variadic patterns like summing, concatenation, or logical checks.

Real-World Scenario

Build a variadic logging function that prints all arguments separated by spaces.

Quick Snippet

snippet.cpp
#include <iostream>

template<typename... Args>
void log(Args&&... args) {
    (std::cout << ... << args) << "\n";  // Binary left fold
}

template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Unary right fold
}

Full Example

fold_expressions.cpp
// CodeNebula - C++17 Example: Fold Expressions
// Compile: g++ -std=c++17 -Wall -Wextra -o example fold_expressions.cpp

// Demonstrates fold expressions over parameter packs. C++17 supports four forms:
//   Unary right fold: (args op ...)     -> a1 op (a2 op (a3 op a4))
//   Unary left fold:  (... op args)     -> ((a1 op a2) op a3) op a4
//   Binary right fold: (args op ... op init)
//   Binary left fold:  (init op ... op args)

#include <iostream>
#include <string>

// ------------------------------------------------------------------
// 1. Unary right fold: sum all arguments  (args + ...)
// ------------------------------------------------------------------
template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // a1 + (a2 + (a3 + a4))
}

// ------------------------------------------------------------------
// 2. Unary left fold: subtract left-to-right  (... - args)
// ------------------------------------------------------------------
template <typename... Args>
auto subtract_left(Args... args) {
    return (... - args);  // ((a1 - a2) - a3) - a4
}

// ------------------------------------------------------------------
// 3. Binary left fold with init: product with initial value
// ------------------------------------------------------------------
template <typename... Args>
auto product(Args... args) {
    return (1 * ... * args);  // ((1 * a1) * a2) * a3
}

// ------------------------------------------------------------------
// 4. Fold with comma operator: print all arguments
// ------------------------------------------------------------------
template <typename... Args>
void print_all(Args... args) {
    ((std::cout << "  " << args << std::endl), ...);
}

// ------------------------------------------------------------------
// 5. Fold with logical AND: all positive?
// ------------------------------------------------------------------
template <typename... Args>
bool all_positive(Args... args) {
    return ((args > 0) && ...);
}

// ------------------------------------------------------------------
// 6. Fold with logical OR: any negative?
// ------------------------------------------------------------------
template <typename... Args>
bool any_negative(Args... args) {
    return ((args < 0) || ...);
}

// ------------------------------------------------------------------
// 7. Binary fold for string concatenation with separator
// ------------------------------------------------------------------
template <typename... Args>
std::string join(Args... args) {
    return (std::string{} + ... + (std::string(", ") + args));
}

int main()
{
    std::cout << "=== unary right fold (sum) ===" << std::endl;
    std::cout << "  sum(1,2,3,4): " << sum(1, 2, 3, 4) << std::endl;
    std::cout << "  sum(1.5, 2.5): " << sum(1.5, 2.5) << std::endl;

    std::cout << "\n=== unary left fold (subtract) ===" << std::endl;
    std::cout << "  subtract_left(100,20,5,3): " << subtract_left(100, 20, 5, 3) << std::endl;

    std::cout << "\n=== binary left fold (product) ===" << std::endl;
    std::cout << "  product(2,3,4): " << product(2, 3, 4) << std::endl;
    std::cout << "  product(): " << product() << std::endl;  // empty pack, returns init (1)

    std::cout << "\n=== comma fold (print_all) ===" << std::endl;
    print_all(42, "hello", 3.14, 'X');

    std::cout << "\n=== logical AND fold ===" << std::endl;
    std::cout << "  all_positive(1,2,3): " << std::boolalpha << all_positive(1, 2, 3) << std::endl;
    std::cout << "  all_positive(1,-2,3): " << all_positive(1, -2, 3) << std::endl;

    std::cout << "\n=== logical OR fold ===" << std::endl;
    std::cout << "  any_negative(1,2,3): " << std::boolalpha << any_negative(1, 2, 3) << std::endl;
    std::cout << "  any_negative(1,-2,3): " << any_negative(1, -2, 3) << std::endl;

    std::cout << "\n=== string join fold ===" << std::endl;
    std::cout << "  " << join("Alice", "Bob", "Carol") << std::endl;

    return 0;
}

/*
Expected output:

=== unary right fold (sum) ===
  sum(1,2,3,4): 10
  sum(1.5, 2.5): 4

=== unary left fold (subtract) ===
  subtract_left(100,20,5,3): 72

=== binary left fold (product) ===
  product(2,3,4): 24
  product(): 1

=== comma fold (print_all) ===
  42
  hello
  3.14
  X

=== logical AND fold ===
  all_positive(1,2,3): true
  all_positive(1,-2,3): false

=== logical OR fold ===
  any_negative(1,2,3): false
  any_negative(1,-2,3): true

=== string join fold ===
  , Alice, Bob, Carol
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example fold_expressions.cpp