4 minute read

1. 템플릿 확장

✏️클래스 템플릿, 배열 클래스 템플릿

  • 배열 클래스에 저장하고 싶은 자료형이 클래스 템플릿이어도 가능하다.

    • BoundCheckArray<Point<int>> arr(50);이런식으로 만들 수 있다.

    • BoundCheckArray<Point<int>*> arr(50); 이것도 된다.

    • typedef Point<int>* POINT_PTR;로 해놓고 BoundCheckArray<POINT_PTR> arr(50); 이렇게 해도 된다.

✏️템플릿 클래스 인자로 받는 함수, friend

  • 예제

      #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; 
      	}
    
          friend Point<int> operator+(const Point<int>&, const Point<int>&);  // 클래스 템플릿도 일반함수에 friend선언이 가능하다
      	friend ostream& operator<<(ostream& os, const Point<int>& pos);
      };
    
      Point<int> operator+(const Point<int>& pos1, const Point<int>& pos2)                // +연산자 오버로딩
      {       // Point<int>는 템플릿 클래스에 자료형이 선언되었으므로 이 객체 둘을 인자로 전달받는 일반 함수이다
          return Point<int>(pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos);
      }
    
      ostream& operator<<(ostream& os, const Point<int>& pos)     // <<연산자 오버로딩
      {
          os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl;
          return os;
      }
    
      int main(void)
      {	
          Point<int> pos1(2, 4);      //[2,4]         
          Point<int> pos2(4, 8);      //[4,8]
          Point<int> pos3=pos1+pos2;  //[6,12]
          cout<<pos1<<pos2<<pos3;        
          return 0;
      }
    

2. 클래스 템플릿 특수화

  • 이유 : 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 기능, 행동을 적용하기 위해서이다.

  • 템플릿을 구성하는 멤버함수, 변수를 다르게 행동하도록 정의할 수 있다.

#include <iostream>
#include <cstring>
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; 
	}
};

template <typename T>
class SimpleDataWrapper 
{
private:
	T mdata;
public:
	SimpleDataWrapper(T data) : mdata(data)
	{ }
	void ShowDataInfo(void)
	{
		cout<<"Data: "<<mdata<<endl;
	}
};

template<>
class SimpleDataWrapper <char*>
{
private:
	char* mdata;
public:
	SimpleDataWrapper(char* data)
	{
		mdata=new char[strlen(data)+1];
		strcpy(mdata, data);
	}
	void ShowDataInfo(void)
	{
		cout<<"String: "<<mdata<<endl;
		cout<<"Length: "<<strlen(mdata)<<endl;
	}
	~SimpleDataWrapper()
	{
		delete []mdata;
	}
};

template<>
class SimpleDataWrapper <Point<int>>
{
private:
	Point<int> mdata;
public:
	SimpleDataWrapper(int x, int y) : mdata(x, y)
	{ }
	void ShowDataInfo(void)
	{
		mdata.ShowPosition();
	}
};

int main(void)
{	
	SimpleDataWrapper<int> iwrap(170);      //일반 클래스 템플릿이 적용되어 만들어진 템플릿 클래스의 객체
	iwrap.ShowDataInfo();                   // Data: 170

	SimpleDataWrapper<char*> swrap("Class Template Specialization");        //특수화된 클래스 템플릿이 적용되어 만들어진 템플릿 클래스 객체
	swrap.ShowDataInfo();                   //String: Class Template Specialization
                                            //Length: 29

	SimpleDataWrapper<Point<int>> poswrap(3, 7);   //특수화된 클래스 템플릿이 적용되어 만들어진 템플릿 클래스 객체
	poswrap.ShowDataInfo();     //[3, 7]
	return 0;
}

✏️부분 특수화

  • 부분적으로만 특수화 시킬 수 있다.
#include <iostream>
using namespace std;

template <typename T1, typename T2>     //템플릿 매개변수 선언
class MySimple 
{ 
public:
	void WhoAreYou()
	{
		cout<<"size of T1: "<<sizeof(T1)<<endl;
		cout<<"size of T2: "<<sizeof(T2)<<endl;
		cout<<"<typename T1, typename T2>"<<endl;
	}
};

template<>
class MySimple<int, double>
{
public:
	void WhoAreYou()
	{
		cout<<"size of int: "<<sizeof(int)<<endl;
		cout<<"size of double: "<<sizeof(double)<<endl;
		cout<<"<int, double>"<<endl;
	}
};


template<typename T1>       //여기서 double만 특수화 되었다. 
class MySimple<T1, double>
{
public:
	void WhoAreYou()
	{
		cout<<"size of T1: "<<sizeof(T1)<<endl;
		cout<<"size of double: "<<sizeof(double)<<endl;
		cout<<"<T1, double>"<<endl;
	}
};


int main(void)
{
	MySimple<char, double> obj1;
	obj1.WhoAreYou();
	MySimple<int, long> obj2;
	obj2.WhoAreYou();
	MySimple<int, double> obj3;
	obj3.WhoAreYou();
	return 0;
}
  • 전체 특수화가 부분 특수화보다 우선시 된다.
  • 즉, <int, double>템플릿이 있고, <T , double>템플릿이 있을 때, <int, double>은 전체 특수화인 <int,double>템플릿 클래스의 객체가 된다.

3. 템플릿 인자

  • 결정되지 않은 자료형을 의미하는 T, T1, T2같은 애들을 템플릿 매개변수라고 한다.

  • 템플릿 매개변수에는 변수 선언이 올 수 있다.

#include <iostream>
using namespace std;

template <typename T, int len>      //템플릿 매개변수 선언에 변수의 선언이 명시됨
class SimpleArray
{
private:
	T arr[len];
public:
	
	T& operator[] (int idx)
	{
		return arr[idx];
	}
	T& operator=(const T&ref)
	{
		for(int i=0; i<len; i++)
			arr[i]=ref.arr[i];
	}
};

int main(void)
{	
	SimpleArray<int, 5> i5arr1;
	for(int i=0; i<5; i++)
		i5arr1[i]=i*10;

	SimpleArray<int, 5> i5arr2;
	i5arr2=i5arr1;
	for(int i=0; i<5; i++)
		cout<<i5arr2[i]<<", ";
	cout<<endl;

	SimpleArray<int, 7> i7arr1;
	for(int i=0; i<7; i++)
		i7arr1[i]=i*10;

	SimpleArray<int, 7> i7arr2;
	i7arr2=i7arr1;
	for(int i=0; i<7; i++)
		cout<<i7arr2[i]<<", ";
	cout<<endl;
	return 0;
}

  • <int, 5>와 <int, 7>에 대한 각각의 템플릿 클래스가 별도로 만들어진다
  • 생성자로 넣어주면 되는데 이렇게 작성한 코드가 같는 의미는, 자료형과 길이가 같은 배열 객체만 대입과 복사가 허용되기 때문에 길이가 다른 두 배열 객체간의 대입 및 복사에 대한 부분을 신경 쓸 필요가 없다는 것이다.

  • 템플릿 매개변수에 디폴트 값 지정도 가능하다
      <typename T, int len = 7> //이런거 됨
      class SimpleArray
      {
      private:
          T arr[len];
      public:
    		
          T& operator[] (int idx)
          {
              return arr[idx];
          }
          T& operator=(const T&ref)
          {
              for(int i=0; i<len; i++)
                  arr[i]=ref.arr[i];
          }
      };
    

4. 템플릿과 static

✏️함수 템플릿과 static

  • 함수 템플릿 내 선언된 static 변수는 템플릿 함수 별로 각각 존재하게 된다.
  • 예제

      #include <iostream>
      using namespace std;
    
      template <typename T>
      void ShowStaticValue(void)
      {
          static T num=0;
          num+=1;
          cout<<num<<" ";
      }
    
      int main(void)
      {
          ShowStaticValue<int>();		// 1
          ShowStaticValue<int>();		// 2
          ShowStaticValue<int>();		// 3
          cout<<endl;
    
          ShowStaticValue<long>();	// 1
          ShowStaticValue<long>();	// 2
          ShowStaticValue<long>();	// 3
          cout<<endl;
    
          ShowStaticValue<double>();	// 1
          ShowStaticValue<double>();	// 2
          ShowStaticValue<double>();	// 3
          return 0;
      }
    

✏️클래스 템플릿과 static멤버변수

  • static 멤버변수 : 선언된 클래스의 객체간 공유가 가능한 변수

  • 템플릿 클래스 별로 static 멤버함수를 유지하게 된다
  • 예제

      #include <iostream>
      using namespace std;
    
      template <typename T>
      class SimpleStaticMem
      {
      private:
          static T mem;
    
      public:
          void AddMem(int num) { mem+=num; }
          void ShowMem() { cout<<mem<<endl; }
      } ;
    
      template <typename T> 
      T SimpleStaticMem<T>::mem=0;		// static 멤버 초기화 문장
    
      /*
      template<> 
      long SimpleStaticMem<long>::mem=5;	// static 멤버의 초기화에 대해서도 특수화를 진행할 수 있다.
      */
    
      int main(void)
      {
          SimpleStaticMem<int> obj1;
          SimpleStaticMem<int> obj2;
          obj1.AddMem(2);
          obj2.AddMem(3);
          obj1.ShowMem();
    
          SimpleStaticMem<long> obj3;
          SimpleStaticMem<long> obj4;
          obj3.AddMem(100);
          obj4.ShowMem();
          return 0 ;
      }
    

✏️template, template<>

  • 템플릿 관련 정의에는 template<typename T> 혹은 template<> 와 같은 선언을 써서, 컴파일러에게 템플릿을 정의하고 있음을 알린다

    template : 템플릿 정의에 T가 등장하는 경우, 이 선언을 통해 T가 의미하는 바를 알려야한다 template<> : 특수화된 템플릿의 경우, T라는 문자가 등장하지 않지만 템플릿의 정의임을 알려야 하기 때문에 이 선언을 명시한다.

  • static 멤버의 초기화에 대해서도 특수화를 진행할 수 있다.

    template<>
    long SimpleStaticMem::mem=5;

Tags: ,

Categories:

Updated: