본문 바로가기

Programming/CPP11&14

[C++11] 가변인자 템플릿(Variadic Templates)

반응형

C와 C++에는 가변인자 함수가 존재합니다.

...이라는 생략 부호를 사용하는 특이한 형태의 함수입니다.

C/C++에는 대표적인 가변인자 함수인 printf가 존재합니다.

가변인자 함수에 대해서는 아래의 링크를 참조하시면 됩니다.

2014/12/07 - [Programming/C&C++] - 가변 인자 함수의 사용법(vprintf, vsprintf)

2014/12/07 - [Programming/C&C++] - 가변 인자(Variable Arguments) 내부 구조

C++11에는 가변인자를 템플릿에도 적용이 가능하게 되었습니다.

가변인자 템플릿은 다음과 같은 형식으로 되어 있습니다.

template <typename... Arguments> returntype functionname(Arguments... args);
template<typename... Arguments> class classname;

기존의 가변인자 함수와는 약간 형식이 다릅니다.

가변인자 템플릿을 활용하는 예제는 다음과 같습니다.

#include <iostream>

using namespace std;

void print()
{
	cout << endl;
}

template <typename T> void print(const T& t)
{
	cout << t << endl;
}

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest)
{
	cout << first << ", ";
	print(rest...); // recursive call using pack expansion syntax
}

int main()
{
	print(); // calls first overload, outputting only a newline
	print(1); // calls second overload

	// these call the third overload, the variadic template, 
	// which uses recursion as needed.
	print(10, 20);
	print(100, 200, 300);
	print("first", 2, "third", 3.14159);
}

MSDN의 가변인자  템플릿 함수 예제입니다.

아무것도 받지 않는 print() 함수가 존재하고 템플릿으로 선언된 1개만 전달받는 함수가 있습니다.

그 아래에 존재하는 함수가 가변인자 템플릿 함수입니다.

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest)

const Rest&... rest라고 되있는 부분이 파라미터 팩(Parameter Pack)이라고 불립니다.

그리고 함수 안에 존재하는 print(rest...) 부분이 팩 확장(Pack Expansion)입니다.

그리고 가변인자 템플릿은 팩 확장을 통해서 재귀적인 형태로 계속 호출이 됩니다.

아래 코드가 실행될 때 순서를 확인하면 다음과 같습니다.

print("first", 2, "third", 3.14159);

먼저 가변인자 템플릿 함수가 실행됩니다.

맨 처음의 "first"가 first 인자로 들어가고 나머지가 rest로 들어가게 됩니다.

그리고 "first"가 cout으로 출력되고 나머지 변수가 rest...을 통해서 다시 확장이 됩니다.

다시 2가 first로 들어가고 나머지가 rest가 됩니다.

그렇게 계속적으로 가변인자 템플릿 함수를 호출하다가 마지막에 3.14159만 남을 때 일반 템플릿 함수를 호출합니다.

그리고 함수 호출이 종료가 되게 됩니다.

boost 라이브러리에서 C++11 표준으로 채택된 tuple 역시 가변인자 템플릿을 활용해서 구현이 됩니다.

tuple을 가변인자 템플릿을 통해서 구현하는 원리는 다음 소스 코드를 통해서 확인할 수 있습니다.

내용을 분석하다보면 템플릿에 대한 이해를 좀 더 넓히는데 도움이 됩니다.

또한 가변인자 템플릿이 어떤 방식으로 확장을 해나가는지도 확인이 가능합니다.

#include <iostream>

template<int...>
struct seq
{
};

template<int max, int... s>
struct make_seq : make_seq < max - 1, max - 1, s... >
{
};

template<int... s>
struct make_seq < 0, s... >
{
	typedef seq<s...> type;
};

template<int max>
using MakeSeq = typename make_seq<max>::type;

template<int x, typename Arg>
struct foo_storage
{
	foo_storage()
	{
		std::cout << "foo_storage" << std::endl;
	}
	Arg data;
};

template<typename Seq, typename... Args>
struct foo_helper
{
	foo_helper()
	{
		std::cout << "foo_helper1" << std::endl;
	}
};

template<int s0, int... s, typename A0, typename... Args>
struct foo_helper<seq<s0, s...>, A0, Args...> :	foo_storage<s0, A0>, foo_helper < seq<s...>, Args... >
{
	foo_helper()
	{
		std::cout << "foo_helper2" << std::endl;
	}
};

template<typename... Args>
struct foo : foo_helper<MakeSeq<sizeof...(Args)>, Args...>
{
	foo()
	{
		std::cout << "foo" << std::endl;
	}
};

template<int N, typename T>
T& get(foo_storage<N, T>& f)
{
	return f.data;
}

template<int N, typename T>
T const& get(foo_storage<N, T> const& f)
{
	return f.data;
}

void main()
{
	foo<int, float, int> f;

	get<0>(f) = 7;
	get<1>(f) = 3.14f;
	get<2>(f) = 6;
	std::cout << get<0>(f) << ", " << get<1>(f) << ", " << sizeof(f) <<"\n";
}

C++11과 14를 거치면서 상당히 많은 문법이 추가되었습니다.

가변인자 템플릿도 그 중 하나로 유용하게 사용이 가능할 것으로 보입니다.

가변인자 템플릿을 활용하는 tuple은 더 효율적으로 개발이 가능하게 해줍니다.

반응형