C++ Fold Expressions | "Parameter Pack Folding" Guide

C++ Fold Expressions | "Parameter Pack Folding" Guide

이 글의 핵심

C++17 fold expressions: unary/binary folds over parameter packs (+, &&, comma)—less boilerplate than recursive templates.

What are Fold Expressions?

C++17 fold expressions are a syntax for “folding” a parameter pack in variadic templates using an operator. Without requiring recursive templates, you can apply operators like +, &&, <<, etc., to the entire pack at once. This can make your code more concise, especially after mastering template basics.

// Before C++17: Recursion
template<typename T>
T sum(T value) {
    return value;
}

template<typename T, typename... Args>
T sum(T first, Args... args) {
    return first + sum(args...);
}

// C++17: Fold Expression
template<typename... Args>
auto sum(Args... args) {
    return (... + args);  // Unary left fold
}

cout << sum(1, 2, 3, 4, 5) << endl;  // 15

4 Types of Folding

// 1. Unary right fold: (args op ...)
template<typename... Args>
auto sum1(Args... args) {
    return (args + ...);  // ((1 + 2) + 3) + 4
}

// 2. Unary left fold: (... op args)
template<typename... Args>
auto sum2(Args... args) {
    return (... + args);  // 1 + (2 + (3 + 4))
}

// 3. Binary right fold: (args op ... op init)
template<typename... Args>
auto sum3(Args... args) {
    return (args + ... + 0);  // ((1 + 2) + 3) + 0
}

// 4. Binary left fold: (init op ... op args)
template<typename... Args>
auto sum4(Args... args) {
    return (0 + ... + args);  // 0 + (1 + (2 + 3))
}

Supported Operators

// Arithmetic operators
(... + args)   // Addition
(... - args)   // Subtraction
(... * args)   // Multiplication
(... / args)   // Division

// Logical operators
(... && args)  // AND
(... || args)  // OR

// Bitwise operators
(... & args)   // AND
(... | args)   // OR
(... ^ args)   // XOR

// Comparison operators
(... < args)   // Less than
(... > args)   // Greater than

// Miscellaneous
(... , args)   // Comma

Practical Examples

Example 1: Printing

template<typename... Args>
void print(Args... args) {
    (cout << ... << args) << endl;
}

print("x = ", 42, ", y = ", 3.14);
// x = 42, y = 3.14

Example 2: Checking All Values

template<typename... Args>
bool all(Args... args) {
    return (... && args);
}

cout << all(true, true, true) << endl;   // 1
cout << all(true, false, true) << endl;  // 0

Example 3: Adding to a Container

template<typename T, typename... Args>
void push_back_all(vector<T>& vec, Args... args) {
    (vec.push_back(args), ...);
}

vector<int> vec;
push_back_all(vec, 1, 2, 3, 4, 5);
// vec = {1, 2, 3, 4, 5}

Example 4: Calling Functions

template<typename... Funcs>
void call_all(Funcs... funcs) {
    (funcs(), ...);
}

void f1() { cout << "f1" << endl; }
void f2() { cout << "f2" << endl; }
void f3() { cout << "f3" << endl; }

call_all(f1, f2, f3);
// f1
// f2
// f3

Example 5: Range Checking

template<typename T, typename... Args>
bool in_range(T value, T min, T max, Args... args) {
    return ((value >= min && value <= max) || ... || 
            (value >= args && value <= args));
}

// Or simplified
template<typename T, typename... Args>
bool contains(T value, Args... args) {
    return ((value == args) || ...);
}

cout << contains(3, 1, 2, 3, 4, 5) << endl;  // 1
cout << contains(6, 1, 2, 3, 4, 5) << endl;  // 0

Example 6: Minimum/Maximum Value

template<typename... Args>
auto min(Args... args) {
    return (args < ...);  // Incorrect!
}

// Correct implementation
template<typename T>
T min(T value) {
    return value;
}

template<typename T, typename... Args>
T min(T first, Args... args) {
    T rest = min(args...);
    return first < rest ? first : rest;
}

// Or using std::min
template<typename... Args>
auto minimum(Args... args) {
    return std::min({args...});
}

cout << minimum(5, 2, 8, 1, 9) << endl;  // 1

Comma Operator

template<typename... Args>
void process(Args... args) {
    int dummy[] = {(cout << args << " ", 0)...};
}

process(1, 2, 3, 4, 5);
// 1 2 3 4 5

// Or using Fold Expression
template<typename... Args>
void process2(Args... args) {
    ((cout << args << " "), ...);
}

Common Issues

Issue 1: Empty Pack

// ❌ Empty pack (only some operators allowed)
template<typename... Args>
auto sum(Args... args) {
    return (... + args);  // Error if pack is empty
}

// ✅ Provide an initial value
template<typename... Args>
auto sum(Args... args) {
    return (0 + ... + args);  // 0 if pack is empty
}

// Operators that allow empty packs: &&, ||, ,
(... && args)  // true
(... || args)  // false
(... , args)   // void()

Issue 2: Operator Precedence

// ❌ Confusing precedence
template<typename... Args>
auto func(Args... args) {
    return (... + args * 2);  // Error
}

// ✅ Explicit parentheses
template<typename... Args>
auto func(Args... args) {
    return (... + (args * 2));
}

Issue 3: Type Mismatch

// ❌ Type mismatch
auto x = (1 + ... + 3.14);  // int + double

// ✅ Explicit type
template<typename T, typename... Args>
T sum(Args... args) {
    return (T(0) + ... + T(args));
}

Fold vs Recursion

// Recursion (complex)
template<typename T>
void print(T value) {
    cout << value << endl;
}

template<typename T, typename... Args>
void print(T first, Args... args) {
    cout << first << " ";
    print(args...);
}

// Fold (simple)
template<typename... Args>
void print(Args... args) {
    ((cout << args << " "), ...);
    cout << endl;
}

FAQ

Q1: When should I use Fold Expressions?

A:

  • When working with variadic templates
  • To handle parameter packs
  • To replace recursion

Q2: Are all operators supported?

A: Most binary operators are supported. Unary operators are not.

Q3: What about performance?

A: Performance is similar to recursion, but compile time can be shorter.

Q4: What about empty packs?

A: Only &&, ||, and , are allowed. Other operators require an initial value.

Q5: What about pre-C++17?

A: Use recursive templates.

Q6: Where can I learn more about Fold Expressions?

A:

  • “C++17 The Complete Guide”
  • cppreference.com
  • “Effective Modern C++”

Related Posts: Advanced Variadic Templates, Template Basics, Template Argument Deduction.

## 같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

- [C++ 가변 인자 템플릿 고급 | pack 확장과 fold 표현식](/blog/cpp-variadic-template-advanced/)
- [C++ Fold Expression 완벽 가이드 | 단항·이항·쉼표 fold·커스텀 연산자 실전](/blog/cpp-series-44-2-fold-expressions/)
- [C++ 템플릿 | "제네릭 프로그래밍" 초보자 가이드](/blog/cpp-template-basics/)
- [C++ 템플릿 인자 추론 | template argument deduction 가이드](/blog/cpp-template-argument-deduction/)

---

---

## 이 글에서 다루는 키워드 (관련 검색어)

C++, fold, variadic, template, parameter-pack 등으로 검색하시면 이 글이 도움이 됩니다.