* 정의

 

가변인자 템플릿(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

 

+ Recent posts