본문 바로가기
Programming/C++

템플릿(Template)

by OKOK 2017. 8. 1.

함수 템플릿은 함수를 만들어 낸다. 함수의 기능은 결정되어 있지만, 자료형은 결정되어 있지 않아서 결정해야 합니다. 함수 템플릿이라는 것은 함수를 만드는 도구가 됩니다. 함수 템플릿도 다양한 자료형의 함수를 만들어 낼 수 있습니다. T는 자료형을 결정짓지 않겠다는 의미로 사용한 것입니다. 즉, 함수를 만들어 내는 템플릿을 정의하기 위해서 사용된 것입니다. 


#include <iostream>

using namespace std;


template <typename T>

T Add(T num1, T num2)

{

return num1 + num2;

}


int main(void)

{

cout << Add<int>(15, 20) << endl;

cout << Add<double>(2.9, 3.7) << endl;

cout << Add<int>(3.2, 3.2) << endl;

cout << Add<double>(3.14, 2.75) << endl;

return 0;


T를 int로 해서 만들어진 Add 함수를 호출합니다. 그럼 함수를 템플릿으로 정의하면, 매 호출문장마다 함수를 만들게 되나요? 컴파일 할 때 함수가 만들어지므로 속도의 감소가 발생한다. 그런데 이는 컴파일 속도이지 실행속도가 아닙니다. 함수 템플릿이라 합니다. 템플릿을 기반으로 컴파일러가 만들어 내는 다음 유형의 함수들을 가리켜 템플릿 함수라 합니다. 


#include <iostream>

using namespace std;


template <typename T>

T Add(T num1, T num2)

{

cout << "T Add(T num1, T num2)" << endl;

return num1 + num2;

}


int Add(int num1, int num2)

{

cout << "Add(int num1, int num2)" << endl;

return num1 + num2;

}


double Add(double num1, double num2)

{

cout << "Add(double num1, double num2)" << endl;

return num1 + num2;

}


int main(void)

{

cout << Add(5, 7) << endl;

cout << Add(3.7, 7.5) << endl;

cout << Add<int>(5, 7) << endl;

cout << Add<double>(3.7, 7.5) << endl;

return 0;


일반함수의 형태로 Add 함수가 정의되었습니다. 이렇듯 템플릿이 정의되어도 일반함수를 정의할 수 있습니다. 템플릿 함수와 일반함수는 구분이 되기 때문입니다. 함수가 정의되어 있지 않으면 템플릿 함수가 호출되는데, 일반함수가 정의되어 있기 때문에 일반함수가 호출됩니다. 일반함수가 정의되었기 때문에 <인트>, <더블>의 표시를 통해서 템플릿 함수의 호출을 명시해야 합니다. 템플릿 함수는 컴파일러에 의해서 생성된 함수이기 때문에 생성된 함수으로도 불립니다. 템플릿 클래스 역시 생성된 클래스로도 불립니다. 


둘 이상의 형에 대해 템플릿 선언하기

#include <iostream>

using namespace std;


template <class T1, class T2>

void ShowData(double num)

{

cout << (T1)num << ", " << (T2)num << endl;

}


int main(void)

{

ShowData<char, int>(65);

ShowData<char, int>(67);

ShowData<char, double>(68.9);

ShowData<short, double>(69.2);

ShowData<short, double>(70.4);

return 0;


쉼표를 이용하여 둘 이상의 템플릿 타입을 명시할 수 있습니다. 키워드 typename을 대신해서 키워드 class 를 사용하였습니다. 함수 템플릿의 매개변수 조차도 기본 자료형으로 선언될 수 있습니다. 인자로 전달된 num의 값을 T1과 T2로 명시되는 자료형으로 형 변환해서 출력하고 있습니다. 함수 템플릿은 매개변수 형이 더블로 선언되었기 때문에 전달되는 인자를 통해서 T1 과 T2의 자료형을 결정짓지 못합니다. 따라서 이러한 경우에는 템플릿 함수의 호출형식을 완전히 갖춰서 호출해야 합니다.


#include<iostream>

#include<cstring>

using namespace std;


template <typename T>

T Max(T a, T b)

{

return a > b ? a : b;

}


template <>

char* Max(char* a, char* b)

{

cout << "char* Max<char*>(char* a, char( b)" << endl;

return strlen(a) > strlen(b) ? a : b;

}


template <>

const char* Max(const char* a, const char* b)

{

cout << "const char* Max<const char(>(const char* a, const char* b)" << endl;

return strcmp(a, b) > 0 ? a : b;

}


int main(void)

{

cout << Max(11, 15) << endl;

cout << Max('T', 'Q') << endl;

cout << Max(3.5, 7.5) << endl;

cout << Max("Simple", "Best") << endl;


char str1[] = "Simple";

char str2[] = "Best";

cout << Max(str1, str2) << endl;

return 0;


함수 템플릿 Max 를 char* 형에 대해서 특수화 하였습니다. 함수 템플릿 const char* 형에 대해서 특수화하였습니다. 문자열의 선언으로 인해서 반환되는 주소 값의 포인터 형은 const char* 입니다. 이 문장에 의해 호출되는 함수는 위의 함수입니다. str1 과 str2는 변수의 형태로 선언되었습니다. 따라서 str1 과 str2의 포인터 형은 char* 입니다. 따라서 이 문장에 의해 호출되는 함수는 아래 Max 함수입니다. 


클래스 템플릿


#include <iostream>

using namespace std;


template <typename T>

class Point

{

private:

T xpos, ypos;

public:

Point(T x=0, T y=0) : xpos(x), ypos(y)

{ }

void ShowPosition() const

{

cout << '[' << xpos << ", " << ypos << ']' << endl;

}

};


int main(void)

{

Point<int> pos1(3, 4);

pos1.ShowPosition();


Point<double> pos2(2.4, 3.6);

pos2.ShowPosition();


Point<char> pos3('P', 'F');

pos3.ShowPosition();

return 0;


정의된 템플릿을 기반으로 컴파일러가 만들어 내느 템플릿 클래스 Point<int>의 객체 생성을 명령하고 있습니다. 여기서 <int>는 T를 int로 하여 만든 템플릿 클래스를 의미합니다. 함수 템플릿과 마찬가지로, 컴파일러는 클래스 템플릿을 기반으로 템플릿 클래스를 만들어 냅니다. 클래스 테믈릿 기반의 객체생성에는 반드시 자료형 정보를 명시하도록 되어 있습니다.


클래스 템플릿의 선언과 정의의 분리


#include <iostream>

using namespace std;


template <typename T>

class Point

{

private:

T xpos, ypos;


public:

Point(T x = 0, T y = 0);

void ShowPosition() const;

};


template <typename T>

Point<T>::Point(T x, T y) : xpos(x), ypos(y)

{ }


template <typename T>

void Point<T>::ShowPosition() const

{

cout << '[' << xpos << ", " << ypos << ']' << endl;

}


int main(void)

{

Point<int> pos1(3, 4);

pos1.ShowPosition();


Point<double> pos2(2.4, 3.6);

pos2.ShowPosition();


Point<char> pos3('P', 'F');

pos3.ShowPosition();

return 0;


일반적인 클래스의 정의와 마찬가지로 매개변수의 디폴트 값은 클래스 템플릿 내에만 표시합니다. 일반적인 클래스의 정의와 마찬가지로 생성자를 외부에 정의할 때에는, 클래스 템플릿의 외부에 정의된 생성자에만 이니셜라이저를 삽입합니다. 


#ifndef __ARRAY_TEMPLATE_H_

#define __ARRAY_TEMPLATE_H_


#include <iostream>

#include <cstdlib>

using namespace std;


template <typename T>

class BoundCheckArray

{

private:

T * arr;

int arrlen;


BoundCheckArray(const BoundCheckArray& arr) { }

BoundCheckArray& operator = (const BoundCheckArray& arr) { }

public:

BoundCheckArray(int len);

T& operator[] (int idx);

T operator[] (int idx) const;

int GetArrLen() const;

~BoundCheckArray();

};


template <typename T>

BoundCheckArray<T>::BoundCheckArray(int len) : arrlen(len)

{

arr = new T[len];

}


template <typename T>

T& BoundCheckArray<T>::operator [](int idx)

{

if (idx<0 || idx>arrlen)

{

cout << "Aray index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}


template <typename T>

T BoundCheckArray<T>::operator[] (int idx) const

{

if (idx < 0 || idx >= arrlen)

{

cout << "Array index out of bound exception" << endl;

exit(1);

}

return arr[idx];

}


template <typename T>

int BoundCheckArray<T>::GetArrLen() const

{

return arrlen;

}


template <typename T>

BoundCheckArray<T>::~BoundCheckArray()

{

delete[]arr;

}

#endif

 


#ifndef __POINT_H_

#define __POINT_H_


#include <iostream>

using namespace std;


class Point

{

private:

int xpos, ypos;

public:

Point(int x = 0, int y = 0);

friend ostream& operator<<(ostream& os, const Point& pos);

};


#endif

 


#include <iostream>

#include "ArrayTemplate.h"

#include "Point.h"

using namespace std;


int main(void)

{

BoundCheckArray<int> iarr(5);

for (int i = 0; i < 5; i++)

iarr[i] = (i + 1) * 11;

for (int i = 0; i < 5; i++)

cout << iarr[i] << endl;


BoundCheckArray<Point> oarr(3);

oarr[0] = Point(3, 4);

oarr[1] = Point(5, 6);

oarr[2] = Point(7, 8);

for (int i = 0; i < oarr.GetArrLen(); i++)

cout << oarr[i];


typedef Point * POINT_PTR;

BoundCheckArray<POINT_PTR> parr(3);

parr[0] = new Point(3, 4);

parr[1] = new Point(5, 6);

parr[2] = new Point(7, 8);

for (int i = 0; i < parr.GetArrLen(); i++)

cout << *(parr[i]);


delete parr[0];

delete parr[1];

delete parr[2];

return 0;



#include<iostream>

#include"Point.h"


using namespace std;


Point::Point(int x, int y) : xpos(x), ypos(y){ }


ostream& operator<<(ostream& os, const Point& pos)

{

os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;

return os;