템플릿(Template)
템플릿이란 ‘틀’이란 뜻 그대로 틀만을 짜주고, 이후 컴파일 과정에서 컴파일러가 알아서 해주는 도구이다.
우린 틀만을 만들어주면 컴파일러가 완성된 코드를 만들어 실행하게 하는 방식으로 하여금 코드의 크기를 줄이고 반복된 코드를 줄인다.
Syntax
선언
template <typename T> 혹은 <class T>
T test(T num1, T num2){
return num1+num2;
}
위 예시를 보면 타입명 대신 T가 들어간 것을 볼 수 있다.
해당 템플릿으로 선언된 함수를 호출 하는 코드는 아래와 같다.
test<int>(1,2); test<double>(1.1,2.2);
위 처럼 test<타입>(..)과 같이 호출하게 되면 T 대신 ‘타입’이 들어가게 되면서 호출된다.
혹은 test(1.1, 2) 이렇게 호출하더라도 컴파일러가 알아서 입력된 데이터가 손실 되지 않는 자료형으로 바꿔주기도 한다.
즉, test(1.1, 2)를 호출한다면 test<dobule>(1.1, 2)가 호출 된다는 뜻이다.
둘 이상의 type의 경우
선언은
template <typename T1, typename T2> 혹은 <class T1, class T2>
T test(T1 num1, T2 num2){
return (T1)num1+(T2)num2;
}
위와 같이 선언하며, 호출시에는
test<int, int>(1, 2); test<int, double>(1, 2.2);
위처럼 각각의 형을 정의해 준다.
대략적인 동작원리
템플릿은 컴파일러가 선언된 템플릿을 기반으로 호출되는 각각의 자료형 마다 함수를 새로 만든다.
즉, test<int>()를 호출하게되면,
int test(int num1, int num2) ...
가 컴파일시 생성된다는 뜻이다.
이는 컴파일 과정에서 시간이 걸리지만 실행에 있어서는 문제가 되지 않는다.
참고로 이렇게 템플릿으로 생성된 함수를 ‘템플릿 함수’라고하며, 함수의 템플릿을 ‘함수 템플릿’이라고 한다.
또한 사용자가 int test(..)를 정의해놓앗다면 컴파일러는 따로 템플릿 함수를 생성하지 않고 정의된 함수를 사용한다.
함수 템플릿의 특수화
앞서 말한것 중에서 자용자가 int test(…)와 같이 미리 정의한것이 있다면 해당 함수를 불러온다고 이야기 했다.
이는 일정 자료형에 대해서 사용자가 다른 동작을 원하는 경우 따로 코드를 작성해 사용할 수 있다는 뜻이다.
위 내용을 종합해만든 아래 코드를 보자.
template <typename T1, class T2> void print(T1 num1, T2 num2); template <> void print<int, double>(int num1, double num2); int main() { print(1, 2); print(1, 2.3); return 0; } template <typename T1, class T2> void print(T1 num1, T2 num2) { std::cout << num1 << " " << num2 << std::endl; } template <> void print<int, double>(int num1, double num2) { std::cout << num1 << " dobule:" << num2 << std::endl; }
템플릿 함수를 선언한 후에,
template<> 아래에선 특별한 경우 (int, double)가 들어올 경우 두 값사이에 ‘double:’ 을 넣도록 하고 있다.
이처럼 몇몇 경우에 대해서 사용자가 따로 특수화를 시켜 다른 동작을 하게 하는 것 또한 가능하다.
파일을 나누어 저장 할 경우 주의사항
컴파일러는 파일별로 컴파일 한다는 것을 알고있다.
하지만 템플릿은 컴파일러가 템플릿을 보고 각각의 형에 따라 함수를 만든다.
즉, 헤더파일에 템플릿를 선언만 해놓고 cpp에서 해당 템플릿을 정의할 경우 헤더따로, cpp 파일 다로 컴파일하기에 에러가 발생한다.
이럴경우 헤더 뿐만아니라 ‘ #include “~~.cpp” ‘와 같이 템플릿이 정의된 cpp파일을 include 해야한다.