On this page
article
C++ Variadic Template
Variadic Template 기본
개념
- Variadic Template은 템플릿 타입 인자의 개수에 제한이 없는 템플릿을 작성할 수 있는 기능
- 즉, 인자의 개수를 가변적으로 받아들이는 템플릿
template<typename ... Ts> class tuple;
template<typename ...Ts>
void f2(Ts ... args);
void f2(const Ts& ... args);
위처럼 ...
문법을 통해 타입과 함수 인자 모두 가변적으로 선언 가능.
여러 타입을 받는 tuple이 이를 활용
Pack Expansion
용어
Ts
: template parameter packargs
: function parameter packsizeof...
: pack 안 요소의 개수
template<typename ... Ts>
void fn(const Ts& ... args);
...
연산자는 앞에 있는 요소를 확장한다. 괄호로 묶으면 전체에 적용된다.
예제
예를 들어, 여러 인자를 함수에 전달하거나 각 인자에 연산을 적용할 수 있다.
template<typename ... Ts>
void fn(Ts ... args) {
foo(args...); // foo(1, 2, -3)
foo((++args)...); // foo(++1, ++2, ++-3) → foo(2, 3, -2)
foo(abs(args)...); // foo(abs(2), abs(3), abs(-2))
}
args...
→ 인자들을 순서대로 펼쳐서 전달(++args)...
→ 각 인자에++
연산 후 전달abs(args)...
→ 각 인자에abs()
적용 후 전달
예제 2
다음과 같이 템플릿 파라미터 팩과 값 팩을 동시에 사용한 예제도 가능
template<typename ... Ts>
struct Outer {
template<Ts ... value>
struct Inner {};
};
Outer<int, double>::Inner<3, 3.4> in;
예제 3: 다중 상속에 활용
가변 템플릿을 사용하면 다중 상속도 간결하게 표현 가능
class A {};
class B {};
template<typename... Ts>
class X : public Ts... {
public:
X(const Ts&... args) : Ts(args)... {}
};
int main() {
A a; B b;
X<A, B> x(a, b);
}
X<A, B>
→class X : public A, public B
로 해석- 생성자도
A(a), B(b)
형태로 확장됨
Recursion (재귀)
Pack Expansion 없이 각 인자에 접근하려면 재귀적 구조를 사용
template<typename T, typename ... Ts>
void print(T value, Ts ... args) {
std::cout << value << " ";
if constexpr (sizeof...(args) > 0)
print(args...);
}
마지막 인자가 없는 버전을 위해 if constexpr
로 분기
sum 만들기
recursion 방식
재귀 방식으로 인자들을 더하는 함수:
int sum() { return 0; }
template<typename T, typename ... Ts>
auto sum(T value, Ts ... args) {
return value + sum(args...);
}
Fold Expression 방식(C++17)
C++17부터는 fold expression
을 사용해 더 간결하게 작성 가능
template<typename ... Ts>
auto sum(Ts ... args) {
auto s1 = (args + ...); // 오른쪽 fold
auto s2 = (... + args); // 왼쪽 fold
auto s3 = (args + ... + 0); // 초기값 포함 fold
auto s4 = (0 + ... + args); // 초기값 포함 fold
return s1;
}
Fold Expression 더 알아보기
출력 함수 예제 (fold)
template<typename ... Ts>
void show(Ts ... args) {
(std::print("{} ", args), ...);
}
std::print
를 사용해 인자들을 출력합니다.
vector에 push (fold)
template<typename T, typename ... Ts>
void push_vec(std::vector<T>& v, Ts&& ... args) {
(v.push_back(std::forward<Ts>(args)), ...);
}
vector template 예제
void f1(int a, int b) { std::this_thread::sleep_for(2s); }
void f2(double d) { std::this_thread::sleep_for(3s); }
template<typename F, typename ... Ts>
decltype(auto) chronometry(F&& f, Ts&& ... args)
{
stopwatch sw;
return std::invoke( std::forward<F>(f), std::forward<Ts> (args)...);
}
int main()
{
chronometry(f1, 5, 6);
chronometry(f2, 3.4);
}