[C++] 템플릿

cheon2308
|2023. 12. 19. 10:22
728x90

황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!

https://search.shopping.naver.com/book/catalog/32436115747

 

C++ Programming : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com


함수 중복의 약점

  • 함수 중복은 편리하지만 약점도 있다. 아래 swap() 함수를 보자.
#include <iostream>
using namespace std;

void myswap(int& a, int& b) {
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}
void myswap(double& a, double& b) {
	double tmp;
	tmp = a;
	a = b;
	b = tmp;
}
// 위의 두 함수는 매개 변수만 다르고 나머지 코드는 동일하다.

int main() {
	int a=4, b=5;
	myswap(a, b); // myswap(int& a, int& b)  호출
	cout << a << '\t' << b << endl;

	double c=0.3, d=12.5;
	myswap(c, d); // myswap(double& a, double& b) 호출
	cout << c << '\t' << d << endl;
}
  • myswap() 함수는 두 값을 서로 교환하는 함수인데, 정수 교환과 double 값 교환을 위해 중복해서 만들게 되었다. 만약, char 타입의 값을 교환할 일이 발생한다면, void myswap(char&a, char&b) 함수를 또 만들어주어야 한다.
  • 매개 변수 타입만 다른 myswap() 함수를 계속 중복 작성한다면, 가독성이 떨어지고 실수의 가능성이 늘며, 알고리즘 수정 시 중복된 모든 함수의 코드를 수정해야하는 번거로움이 발생!
  • 따라서, 일반화시킨 틀(template)를 만들어 사용하자.

일반화와 템플릿 선언

  • 템플릿(template)란 함수나 클래스 코드를 찍어내듯이 생산할 수 있도록 일반화(generic)시키는 도구

 

중복 함수의 일반화
  • template 키워드를 이용하면, 중복 함수들을 일반화시킨 특별한 함수를 만들 수 있다.
  • 이렇게 생성된 함수를 템플릿 함수(template function) 혹은 제네릭 함수(generic function)이라 부른다.

 

템플릿 선언과 제네릭 타입
  • 템플릿 함수나 클래스를 작성 시, template 키워드로 시작하여 <class T>나 <typename T>로 제네릭 타입 T를 선언한다.
template <class T>
template <typename T>

 

  • 제네릭 타입(generic type)이란 C++ 기본 타입이 아니며, 이들을 일반화시킨 새로운 타입으로 일반 타입으로 부르기도 한다.
  • 제네릭 타입은 개발자가 원하는 아무 이름이나 붙이면 된다.
template <class T1, class T2, class T3>

  • 또한 위의 템플릿 선언부는 한 줄에 붙여 써도 상관없다.
template <class T> void myswap(T & a, T & b) {
...
}

 

템플릿으로부터의 구체화

  • 중복 함수들을 템플릿화하는 과정의 역과정을 구체화(specialization)라고 부른다.
  • 컴파일러가 함수의 호출문을 컴파일할 때, 구체화를 통해 제네릭 함수로부터 구체적인 함수의 소스 코드를 만들어낸다.
  • 구체화를 통해 생성된 함수를 구체화된 함수(specialized function)라고 부른다.
int a=4, b=5;
myswap(a, b); // 제네릭 타입 T에 int를 대입하여 구체화시킨 함수를 생성하고 호출

구체화 과정

 

위의 그림은 myswap() 함수 호출문을 컴파일러가 어떻게 처리하는지 보여준다.

  1. 컴파일러는 myswap(a, b); 호출문을 컴파일할 때 myswap() 함수를 찾는다.
  2. 템플릿으로 선언된 myswa() 함수를 발견한다.
  3. 구체화한다.
  4. myswap(a, b);의 함수 호출문에서 실인자 a, b가 모두 int 타입이므로, 템플릿의 제네릭 타입 T에 int를 대입시켜 구체화된 버전의 myswap(int & a, int & b)의 소스 코드를 만들어 낸다.
  5. 구체화된 함수의 소스 코드를 컴파일하고, 이 함수를 호출하도록 컴파일한다.

 

generic VS general
  • 사전적 의미로 generic은 '일반화된' 혹은 '모든 것에 적용할 수 있는' 이라는 뜻으로 사용
  • general은 '보통'이라는 뜻으로서 예외적인 것에 대한 반대 표헌
  • 따라서, 템플릿으로 만든 제네릭 함수(generic function)은 보통 함수(general funcion)라는 뜻이 아니라, 일반화된 함수라는 뜻

역할과 구체화 오류

  • 템플릿 함수는 컴파일되지도 호출되지도 않는, 함수의 틀이다.
  • 템플릿의 역할은 제네릭 함수를 선언하고, 컴피일 시점에 구체화시키기 위한 틀을 만드는 것이다.
  • 템플릿 함수로부터 구체화된 버전의 함수가 컴파일되고 호출된다.

이 때, 템플릿으로부터 함수를 구체화시키는 과정에서 제네릭 타입 T에 유의해야 한다.

template <class T> void myswap(T & a, T & b)
  • 매개 변수 a, b는 모두 타입 T로 선언되어 있기 때문에, myswap()을 호출 시 두 개의 매개 변수 타입이 동일해야 한다.
  • 아래는 매개 변수 s와 t의 타입이 다르기 때문에 잘못된 호출로 컴파일 오류가 발생한다.
int s=4;
double t=5;
myswap(s, t); // 컴파일 오류. s와 t의 타입이 같아야 함

 

템플릿의 장단점과 제네릭 프로그래밍

  • 템플릿은 함수의 작성을 용이하게 하고, 함수 코드의 재사용을 가능하게 하여 소프트웨어의 생산성과 유연성을 높인다. 
  • 다만, 컴파일러에 따라 템플릿이 지원되지 않을 수 있기에 포팅에 취약하고 템플릿과 관련된 컴파일 오류 메시지가 빈약하여 디버깅에 많은 어려움이 발생

한편, 템플릿을 사용하여 제네릭 함수나 제네릭 클래스를 만들고 이를 활용하여 프로그램을 작성하는 새로운 프로그래밍 패러다임이 생겼다.

  • 이를 제네릭 프로그래밍(generic programming) 혹은 일반화 프로그래밍이라고 부른다.

 

제네릭과 매크로와의 차이점은?
  • C++ 템플릿은 C 언어에서 #define으로 정의되는 매크로와 유사하며 템플릿의 역할은 매크로를 통해서도 달성할 수 있다.
  • 그러나 매크로는 복잡한 함수나 클래스를 표현하는데 한계가 있으며, 템플릿보다 사용하기 어렵다.
  • 또한 매크로는 타입 안전성(type-safe)이 확보되지 않기 때문에 실행 중에 부작용(side effect)을 초래할 가능성이 높은 반면, 템플릿은 제네릭 타입에 적용되는 실제 타입을 검사하여 구체화 과정을 거치기 때문에, 타입 안전성을 확보하며 실행 중에 발생하는 오류의 가능성을 차단한다.
728x90