Variadic Templates and Fold Expressions
개요
C++에서 템플릿은 강력한 제네릭 프로그래밍 도구입니다. Variadic Templates는 임의의 개수의 템플릿 인자를 받을 수 있게 하여 템플릿의 유연성을 극대화합니다. 이를 통해 printf
와 같이 가변 인자를 받는 함수를 타입에 안전하게 구현할 수 있습니다.
Fold Expressions는 C++17부터 도입된 기능으로, Variadic Template의 파라미터 팩(parameter pack)을 매우 간결하고 직관적인 방식으로 처리할 수 있게 해주는 구문입니다.
이 문서에서는 Variadic Templates의 기본 개념과 재귀를 이용한 고전적인 처리 방식, 그리고 C++17의 Fold Expressions를 활용한 현대적인 처리 방식을 설명합니다.
C++ 버전별 주요 키워드 도입 시기
- C++11: Variadic Templates 도입
- C++17: Fold Expressions 도입
내용 설명
Variadic Templates (C++11)
Variadic Template은 typename...
또는 class...
키워드를 사용하여 0개 이상의 템플릿 인자를 받는 템플릿을 의미합니다. 이 인자들을 파라미터 팩(parameter pack) 이라고 부릅니다.
template <typename... Args>
void myFunction(Args... args);
파라미터 팩은 그 자체로 직접 사용하기 어렵기 때문에, 보통 팩 확장(pack expansion) 을 통해 개별 인자로 풀어 사용합니다. C++11에서는 주로 재귀 함수 호출을 통해 파라미터 팩을 하나씩 처리했습니다.
아래 concat1
함수는 재귀를 통해 파라미터 팩을 처리하는 전형적인 예시입니다.
concat1(T first, Args... args)
: 첫 번째 인자first
와 나머지 인자 팩args
를 받습니다.first
를 처리하고, 나머지args
를 다시concat1
에 넘겨 재귀 호출합니다.concat1(T first)
: 인자가 하나만 남았을 때 재귀를 멈추는 기본 함수(base case)입니다.
Fold Expressions (C++17)
C++17부터는 Fold Expressions를 사용하여 파라미터 팩을 훨씬 간결하게 처리할 수 있습니다. Fold는 파라미터 팩의 모든 요소에 대해 이항 연산자(binary operator)를 적용하는 구문입니다.
주요 구문은 다음과 같습니다.
- Unary Right Fold:
(... op pack)
->(E1 op (E2 op (E3 op ...)))
- Unary Left Fold:
(pack op ...)
->(((... op E3) op E2) op E1)
- Binary Right Fold:
(pack op ... op init)
- Binary Left Fold:
(init op ... op pack)
예를 들어, (... + args)
는 파라미터 팩 args
의 모든 요소를 오른쪽에서부터 +
연산자로 합산합니다.
예제 코드
#include <iostream>
#include <string>
// C++11: 재귀를 이용한 Variadic Template 처리
template <typename T>
T concat_recursive(T first)
{
return first;
}
template <typename T, typename... Args>
T concat_recursive(T first, Args... args)
{
// 첫 번째 인자와 나머지 인자들을 재귀적으로 합산
return first + concat_recursive(args...);
}
// C++17: Fold Expression을 이용한 처리
template <typename... Args>
auto concat_fold(Args... args)
{
// Unary Right Fold: (arg1 + (arg2 + (arg3 + ...)))
return (... + args);
}
// Unary Left Fold 예시
template <typename... Args>
auto subtract_fold_left(Args... args)
{
// ( (... - arg3) - arg2) - arg1
return (args - ...);
}
// Unary Right Fold 예시
template <typename... Args>
auto subtract_fold_right(Args... args)
{
// (arg1 - (arg2 - (arg3 - ...)))
return (... - args);
}
int main()
{
// 문자열 합치기
std::string s1 = "He";
std::string s2 = "ll";
std::string s3 = "o World";
std::cout << "[Recursive] String: " << concat_recursive(s1, s2, s3) << std::endl;
std::cout << "[Fold] String: " << concat_fold(s1, s2, s3) << std::endl;
std::cout << "---" << std::endl;
// 정수 합치기
std::cout << "[Recursive] Integer: " << concat_recursive(1, 2, 3, 4, 5) << std::endl;
std::cout << "[Fold] Integer: " << concat_fold(1, 2, 3, 4, 5) << std::endl;
std::cout << "---" << std::endl;
// Fold 방향에 따른 연산 순서 차이 (뺄셈)
// Left Fold: (((10 - 5) - 3) - 1) = 1
std::cout << "[Fold Left] Subtract (10, 5, 3, 1): " << subtract_fold_left(10, 5, 3, 1) << std::endl;
// Right Fold: (10 - (5 - (3 - 1))) = 7
std::cout << "[Fold Right] Subtract (10, 5, 3, 1): " << subtract_fold_right(10, 5, 3, 1) << std::endl;
return 0;
}
실행 결과
[Recursive] String: HelloWorld
[Fold] String: HelloWorld
---
[Recursive] Integer: 15
[Fold] Integer: 15
---
[Fold Left] Subtract (10, 5, 3, 1): 1
[Fold Right] Subtract (10, 5, 3, 1): 7
활용팁
- 가독성과 간결성: C++17 이상을 사용한다면 재귀 방식보다 Fold Expressions를 사용하는 것이 코드를 훨씬 간결하고 읽기 쉽게 만듭니다. 컴파일러 최적화 측면에서도 더 유리할 수 있습니다.
- 다양한 연산자 활용:
+
,-
외에도&&
,||
,,
(comma operator) 등 다양한 이항 연산자와 함께 사용할 수 있습니다. 예를 들어, 쉼표 연산자를 사용하면 파라미터 팩에 포함된 모든 함수를 순차적으로 실행할 수 있습니다.template<typename... Funcs> void execute_all(Funcs... funcs) { (..., funcs()); // 모든 함수를 순서대로 호출 }
- Perfect Forwarding: Variadic Templates는
std::forward
와 함께 사용되어 인자의 값 카테고리(lvalue/rvalue)를 그대로 전달하는 Perfect Forwarding을 구현하는 데 핵심적인 역할을 합니다. 이는 팩토리 함수나 wrapper 함수를 만들 때 매우 유용합니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : std::concepts (20) (0) | 2025.09.25 |
---|---|
Modern C++ : Type Traits 1 (11, 14, 17, 20) (0) | 2025.09.24 |
Modern C++ : 템플릿 특수화 (Template Specialization) (98, 11, 17) (0) | 2025.09.23 |
Modern C++ : 복사 및 이동 의미론 (Copy and Move Semantics) (98, 11) (0) | 2025.09.22 |
Modern C++ : 연산자 오버로딩 (Operator Overloading) (98, 11, 20) (0) | 2025.09.21 |
Modern C++ : 클래스 템플릿(Class Template) (98) (0) | 2025.09.20 |
Modern C++ : 다형성(Polymorphism) (98, 11) (0) | 2025.09.19 |
Modern C++ : 상속(Inheritance) (98, 11) (0) | 2025.09.18 |