* 정의
가변인자 템플릿(variadic template)은 0개 이상의(고정 개수가 아닌) 변수를 인수로 사용할 수 있는 클래스나 함수 템플릿의 한 종류이다. 간단히 여러 개의 인수를 사용할 수 있는 템플릿이다.
가변인자 템플릿과 반대로 일반 템플릿은 선언 시 지정된 고정 개수의 매개변수만 가질 수 있다.
* 동작 방식
- 기본 형태
일반적으로 다음과 같이 사용된다.
필자는 직관적으로 이해하기 위해 (타입들... 여러 타입) 을 가진 (여러타입(타입들 ...) ...여러변수)와 같이 이해하였다.
template <typename T, typename... Types>
ReturnType FunctionName(T current, Types... rest)
{
FunctionName(rest...);
// ...
}
...으로 표현되는 부분의 의미
- ...가 왼쪽에 붙는 경우 : 파라미터 팩(parameter pack), 0개 이상의 함수 인자를 나타낸다.
- ...가 오른쪽에 붙는 경우 : 파라미터 팩을 풀어서 인자로 분리하는 역할을 한다.
가변인자들은 인자들을 순회할 수 있고, 인자들의 개수를 알 수 있고, 인덱스를 통해 값에 접근할 수 있으며 템플릿의 인자를 분할할 수 있다.
- 넘겨줄 수 있는 가변인자들의 개수
템플릿에 0개 이상의 가변인자를 넘겨줄 수 있다. 0개도 가능하다. 즉 다음과 같은 형식도 허용된다.
template<typename... Types>
class Tuple { /* ... */ };
Tuple<> emptyTuple;
1개이상으로 보장하고 싶다면 다음과 같이 가변인자 템플릿을 정의하면 된다.
template<typename T, typename... Types>
class Tuple_1 { /* ... */ };
// 컴파일 에러가 발생한다.
//Tuple_1<> tuple_1_0;
Tuple_1<int> tuple_1_1;
Tuple_1<int, float, long long> tuple_1_3;
- 가변인자들의 개수
가변인자들의 개수는 sizeof...()함수를 통해서 알 수 있다.
다음 예시는 가변인자를 받아서 float형으로 평균을 반환하는 함수이다. sizeof...가 활용된다.
float Sum_f(void)
{
return 0.0f;
}
template<typename T, typename... Ts>
float Sum_f(T&& arg, Ts&&... args)
{
return arg + Sum_f(std::forward<Ts>(args)...);
}
template<typename... Ts>
float Average_f(Ts&&... args)
{
return Sum_f(std::forward<Ts>(args)...) / sizeof...(Ts);
}
- 가변인자들의 활용
가변인자 자체는 함수나 클래스의 구현에 쉽게 사용할 수 없다. 그래서 템플릿을 재귀호출을 통해 단계적으로 분할하면서 활용하는 경우가 있다. 이 때문에 가변인자 템플릿은 재귀로 구현되어 있는 경우를 볼 수 있다. 이를 C++17부터는 fold expression이라는 형식으로 매우 간단하게 표현할 수 있도록 지원하고 있다.
@ 재귀 호출
바로 위에 나온 코드도 재귀 호출을 통해 구현되었다.
간단히 받은 인자들을 콘솔창에 출력하는 가변인자 템플릿의 예시
void print(void)
{
cout << " :: print empty\n";
}
template <typename T, typename... Ts>
void print(T&& arg, Ts&&... args)
{
cout << arg << " / ";
print(std::forward<Ts>(args)...);
}
int main(void)
{
print(36.2f, 3000'000'000, 'a', 21000,"test1","test2");
return 0;
}
36.2 / 3000000000 / a / 21000 / test1 / test2 / :: print empty
간단히 설명을 하면 print에 여러 인수를 넘겨주게 되면 컴파일러는 해당 인수를 받을 수 있는 함수를 찾아본다. 결국 가변인자 템플릿의 print함수가 선택되고 다시 비슷한 방식으로 재귀적으로 가변인자 템플릿의 함수가 호출된다. 최종적으로 인자가 없는 버전인 print()가 호출되면서 재귀를 탈출하게 된다.
@ fold expression
fold expression가 어떻게 평가되는지 표로 나타내면 다음과 같다.
| fold | 형식 | 평가 |
| Unary right fold | (E op ...) | (E1 op (... op (EN-1 op EN))) |
| Unary left fold | (... op E) | (((E1 op E2) op ...) op EN) |
| Binary right fold | (E op ... op I) | (E1 op (... op (EN−1 op (EN op I)))) |
| Binary left fold | (I op ... op E) | ((((I op E1) op E2) op ...) op EN) |
단항 왼쪽 예시
template<typename... Args>
bool all(Args... args) { return (... && args); }
bool b = all(true, true, true, false);
//==============================================
template<typename ...Args>
void printer(Args&&... args)
{
(std::cout << ... << args) << '\n';
}
printer(1, 2, 3, "abc");
- 가변인자 템플릿 예시
https://create-new-worlds.tistory.com/303
가변인자 템플릿(Variadic Template) 예시
* printf 일반 가변인자 함수와 비교했을 때 가변인자 템플릿은 타입 안정성을 갖출 수 있다. 일반적으로 우리가 사용하는 printf의 경우 %d와 같이 어떤 형식으로 출력해야할 지 정해줘야 한다. 하
create-new-worlds.tistory.com
'C++' 카테고리의 다른 글
| 템플릿 메타 프로그래밍(Template Meta Programming, TMP) (0) | 2022.05.23 |
|---|---|
| 가변인자 템플릿(Variadic Template) 예시 (0) | 2022.05.23 |
| 템플릿(Template) (0) | 2022.05.23 |
| 람다(Lambda) (0) | 2022.04.11 |
| 완벽 전달(Perfect forwarding)의 실패 (0) | 2022.03.31 |