본문 바로가기
IT/C++(CPP)

CPP 초급 강좌 13. C++ 템플릿 기초, template

by 신림83 2020. 10. 23.
반응형

CPP 초급 강좌 13. C++ 템플릿 기초, template

 이번에는 템플릿

 이전의 함수 오버로딩 function overloading 에서 함수 이름이 같아도 파라매타 등이 차이점이 있으면 작성이 가능함을 배웠었다.

 

먼저

함수 템플릿(function template)

다음과 같은 코드를 보자.

int f1(int a) //a
{
	return a;
}

float f1(float a) //b
{
	return a;
}

int main()
{
	f1(0);
	f1(0.0f);
}

 함수 오버로딩 기능을 사용해 두 함수를 작성했다. a, b를 비교해 보면 각 함수는 반환타입과 파라매타의 형태가 같고, a, b는 그 둘만 서로 다르고 본문 내용도 똑같다. 이럴 경우에 템플릿을 사용하면 좋다.

 

템플릿이란?

 타입만 다르고 구현이 동일하거나, 유사한 함수가 있을 때, 여러버전이 존재한다면 그것을 일일이 코딩하지 말고, 컴파일러가 함수를 생성하게 하는 기술, 컴파일러가 동작할 때, 사용할 함수의 틀(template)이라고 보면 된다.

 개발자는 함수 탬플릿이라는 틀을 만들고, 컴파일러가 템플릿을 사용해서 필요한 함수를 생성하게 하는 것이다.

 

위의 코드를 템플릿을 사용해서 수정해보자.

template<typename T> //a
T f1(T a) 
{
	return a;
}

template<typename AAA> //b
AAA f2(AAA a)
{
	return a+a;
}

int main()
{
	f1<int>(0); //c. int f1(int a)
	f1<float>(0.0f); //d. float f1(float a)
}

 탬플릿을 사용하여 a. f1 이라는 틀을 만들었다.

 b. 는 typename AAA의 이름을 자유롭게 쓸 수도 있다는 걸 보여주고 싶았다. 하지만 T 라고 만이 쓴다. 

a. 의 템플릿을 보면 함수의 반환형과, 인자 모두 T를 사용함을 볼수 있다.

 

c, d. 에서 <>를 이용해서 탬플릿 파라메터를 명시적으로 지정해 준다. 

c 에서는 int 형으로 지정하여 int f1(int a) 함수를 호출하는 것이고,

d 에서는 float 형으로 지정하여 float f1(float a) 호출하는 것이다.

 

해당 함수들은 컴파일러가 필요에 따라 컴파일 시간에 필요한 함수를 탬플릿을 보고 생성한다.

실제 기계어로 된 코드에는 int, float 버전을 함수가 존재한다.

 

용어에 대하여 조금 알아보면

template<typename T> //a 에서 T는 템플릿 파라메타 template parameter 라고 한다.

그리고 실제 컴파일 시간에 함수가 생성되는 것은 템플릿 인스턴스(template instantiation) 라 한다.

 

template<typename T> 코드 데신 template<class T> 라고 표현할 수도 있지만(과거 표준), typename 이라는 것이 더욱 정확한 표현이라는 의견이 많고 요즘 추세는 거의 typename이라 표기한다. 가끔 오래된 코드에 class 라 표현되어 있을 수 있다는 것을 기억하자.

 

다음 코드를 보자.

template<typename T> //a
T f1(T a) 
{
	return a;
}

int main()
{
	f1(0); //c. int f1(int a)
	f1(0.0f); //d. float f1(float a)
}

c, d. 에 템플릿 파라메타 전달이 없다. 이럴 경우는 어떨까? 템플릿 파라메타가 없을 경우는 함수 호출 인자를 보고 컴파일러가 결정을 한다. 이를 추론, 혹은 연역 이라고 지칭한다. 위와 동일한 기계어가 생성된다. 사실 사용자 입장에서는 함수를 호출하고 싶은 거지, 그게 템플릿이든 실제 함수이든 크게 신경 쓰고 싶지 않을 것이다. 

 

클래스 템플릿 Class template

또 코드를 보자.

class Point
{
	int x;
	int y;
};

int main()
{
	Point pt;
}

  Point 라는 클래스가 있고 내부 멤버들은 int로 설정되어 있다. 그런데, 사용자는 float 형태의 포인트가 필요하다. 그럼 다음과 같이 고쳐야 할까?

class Point
{
	float x;
	float y;
};

int main()
{
	Point pt;
}

 근데 또 요구사항이 바뀌어 int 버전을 원한다고 하자... 아니 둘 다 필요하다고? 흠.. 까다롭네

 

이런 걸 원하는가?

class Point 
{
	int x;
	int y;
};

class Point
{
	float x;
	float y;
};

int main()
{
	Point pt;
}

 클래스는 오버라이딩 같은 계념이 없다. 재정의 되지 않는다. 굳이 그런 기능을 원한다면 명시적으로 2개의 클래스가 필요할 것이다.

class Point_Int
{
	int x;
	int y;
};

class Point_Float
{
	float x;
	float y;
};

int main()
{
	Point_Int pt;
	Point_Float pt;
}

이런 것인데, 이것을 탬플릿을 사용해서 수정해보자.

template <typename T>
class Point
{
	T x;
	T y;
};

int main()
{
	Point<int> pt1;
	Point<float> pt2;
}

 필요에 따라서 탬플릿으로 코드를 작성함이 훨씬 깔끔할 수 있다.

클래스의 실제 코드 생성은 함수 탬플릿과 마찮가지로 컴파일 시간에 일어난다.

 

여기서도 추론, 혹은 연역이 가능한가?

 c++17 에서 지원하는 생성자가 있으면 가능하다. 알려면 다른 사전 지식이 필요하다. 이건 이후에

 

템플릿 파라매타 여러게 사용하기

template <typename T1, typename T2>
class Point
{
	T1 x;
	T2 y;
};

int main()
{
	Point<int, float> pt1;
	Point<float, int> pt2;
}

위 예제와 같이 각 다른 파라메타를 설정할 수 있다. 필요에 따라 사용하자.

 

템플릿 파라메타에 정수형 사용하기

template <typename T, int N>
class Point
{
	T x[N];
	T y[N];
};

int main()
{
	Point<int, 10> pt1;

	int x = 100;
	Point<float, x> pt2; //불가 int N은 리터럴만 올수 있음 변수 사용안됨
}

 템플릿 파라메터로 정수형 int를 사용할수 있다. 위 예제와 같이 배열등의 필요에 따라 사용하면된다.

Point<int, 10> pt1; 코드와 같이 리터럴 10 을 넣을 경우에는 동작이 잘 되지만,

Point<float, x> pt2; 코드와 같이 변수형을 컴파일 시간에 알수 없는 값은 사용할수 없다.

 

template 관련해서 진짜 맛보기만 보았다. 템플릿 관련 기술들은 내용도 많고, 상당히 복잡하며, 어렵다. 이후에 기회가 되면 더 써보겠다.

 

공부합시다.

봐주셔서 감사합니다.

반응형

댓글