템플릿

seio·2022년 9월 18일
0

C++ STL

목록 보기
4/17

템플릿은 STL 제너릭 프로그래밍의 핵심입니다. 템플릿은 컴파일 타임에 클라이언트가 여러 타입의 함수나 클래스를 쉽게 생성한다.

템플릿은 함수 템플릿(Function Template)와 클래스 템플릿(Class Template)이 있다.
함수 템플릿은 함수를 만들어내는 틀이며, 클래스 템플릿은 클래스를 만들어내는 틀이다.

여러 자료형의 print 함수를 만들려면 여러 자료형의 매개변수를 반영한 print 함수를 만들어야한다.

void print(int n);
void print(double d);
void print(const char* c);

이러한 함수 오버로딩은 매개변수 타입을 미리 알고 있다는 전제로 만들어진다. 사용자 정의 타입 등을 미리 알지 못하면 함수 오버로딩으로 해결할 수 없다.

템플릿을 이용하면 이 문제를 간단하게 해결할 수 있다.

함수 템플릿

함수 템플릿은 함수 앞에 template 키워드를 붙이면 된다. 형식은 template void print(T a);이다.

template<typename T>
void print (T a){
	cout << a << endl;
}

main{
	print(10);
	print(0.5);
	print("ADVDASV");
}

함수 템플릿 정의는 함수 앞에 template라는 키워드를 붙인다. T는 템플릿 매개변수이며 클라이언트에서 결정한다. 함수 템플릿 template print()으로 실제 함수 세 개를 만들어 낸다. 아래의 3 함수를 template print() 템플릿의 인스터스라 한다.

  • 1) print()

  • 2) print()

  • 3) print<const char*>

    템플릿도 함수처럼 여러 매개변수를 갖을 수 있다.

    template<typename t1, typename t2>
    void print(t1 a, t2 b);
    
    main{
    	print (10, 5.5);
    	print (5.5, "chavb");
    	print ("dfad", 100);
    
    }

    class와 typename
    템플릿임을 지정할 때 template를 template로 사용할 수 있습니다. 현재 대부분의 컴파일러에서 두 형식을 모두 지원한다.
    template 형식은 C++ 표준화 이전부터 사용하던 형식이다. 표준화 이 후 template가 사용되지만, 호환성을 위해 대부분 컴파일러는 template나 template 모두를 지원한다.

    배열 출력 함수 템플릿

    template<typename T, int a>
    void printarray(T* arr){
    	for
    		cout<<arr[i]<<endl;
    }
    
    main{
    	int arr1[5] = {1,2,3,4,5};
    	printarray<int,5>(arr1);
    
    	double arr2[4] = {1.2, 23.32,23.56, 67.2};
    	printarray<double, 4>(arr2);
    }

    함수 템플릿의 매개변수로 타입뿐만 아니라 정수 등도 가능하다.

    단 클라이언트 코드에서 printarray<int,5>(arr1)처럼 명시적으로 호출한다. 이유는 함수 인자가 arr1이라는 정보만을 제공하므로 5라는 템플릿 매개변수 인자를 클라이언트 코드만으로 컴파일러가 출론할 수 없기 때문이다.

함수 템플릿 특수화

특수화는 말 그대로 특수화된 버전의 함수 템플릿을 제공한다.

class point{...};
template<typename T>
void print(T a){...};

main{
point pt(1,2);

	print(1);
	print(435.5);
	print (pt); // ERROR
}

위의 에러를 해결하기 위해 2가지 방법이 있다.

  • 1) point 클래스에 << 연산자 오버로딩 함수를 추가하는 것

  • 2) point 객체만의 특수화된 함수 템플릿을 지원하는 것

    class point{...};
    
    //일반화 함수 템플릿
    template<typename T> 
    void print(T a){...};
    
    //특수화 함수 템플릿
    template<>
    void print(point a){ // 명시적인 코드: void print<point>(point a)
    	a.print();
    }
    
    main{
    	point pt(1,2);
    
    	print (pt); // print<point>(pt) 특수화 버전 호출
    }

클래스 템플릿

클래스 템플릿은 함수 템플릿과 별반 다르지 않다.

클래스 템플릿을 설명하기 위한 정수형 배열을 추상화한 클래스 Array를 예시로 듭니다.

template<typename T>
class Array{
	T* buf;
	int size;
	int capacity;

public:
	explicit Array(int cap=100):buf(0),size(0),capacity(cap){
		buf = new T(capacity); }
	~Array(){
		delete []buf;}
	T operator[](int idx){
		return buf[idx];}
};

main{
	Array<int> iarr;
	Array<double> darr;
	Array<string> sarr;
	
	...
}

디폴트 매개변수 값을 갖는 템플릿

template<typename T= int, int capt =100> // int, 100 디폴트 매개변수 값 지정

클래스 템플릿 특수화

클래스 템플릿 특수화(Specialization)는 함수 템플릿 특수화처럼 일반 버전의 템플릿을 사용할 수 없는 경우나 성능 개선이나 특수화 기능 등을 위해 특수화 버전을 별도로 제공하고자 할 때 사용한다.

template<typename T> //
class objectinfo{
	T data;
public:
	objectinfo(const T& d):data(d){};
voidprint(){};
};

//클래스 템플릿 특수화
template<>
class objectinfo<string>{
 string data;
public:
	objectinfo(const string& d):data(d){};
voidprint(){};
};

템플릿 예제

for_each 문으로 템플릿 예시

	template<typename IterT, typename Func>
void for_each(IterT begin, IterT end, Func pf){
	while(begin!=end)
		pf(*begin++);
}

void printint(int data){...}
void printstring(string data{...}

main{
	for_each<int*,void(*)(int)>(arr,arr+5,printint);
	for_each<string*, void(*)(string)>(sarr,sarr+5.printstring);
}

printint, printstring이 다른 것은 타입뿐이므로 이 출력함수도 템플릿 함수로 작성할 수 있다.

 template<typename IterT, typename Func>
void for_each(IterT begin, IterT end, Func pf){...}
template<typename T>
void print(T data){...};

main{
	for_each(arr,arr+5,print<int>);
	for_each(sarr,sarr+5,print<string>);
}

여기서 주의할 점은 출력 함수의 템플릿 매개변수를 컴파일러가 유추할 수 없으므로 명시적으로 매개변수 인자(print, print)를 지정해줘야 한다.
마지막으로 출력 함수 Print()를 함수 객체로 바꿀수 있다. 함수 객체를 사용하면 부가적인 서비스를 함수 객체가 제공하게 할 수 있다.

template<typename IterT, typename Func>
void for_each(IterT begin, IterT end, Func pf){...}

template<typename T>
struct printFunctor{
	string sep;
	explicit printFunctor(const string &s=" "):sep(s){}
	void operator()(T data) const{cout<<data<<sep;}
}

template<typename T>
void print(T data){
	cout<<data<<endl;
}

main{
	for_each(arr,arr+5,print<int>);
	for_each(sarr, sarr+5,print<string>); 
// or
  for_each(arr,arr+5,printFunctor<int>());
	for_each(sarr, sarr+5,printFunctor<string>("123\n")); 
}

함수 객체는 부가정보를 가질 수 있으므로 sep이라는 출력 패턴 구분자를 가진다. 정수는 디폴트 출력 구분자로""(공백 문자열)을 사용하며 문자열은 출력 구분자로 "123\n"을 사용한다.

profile
personal study area

0개의 댓글