본문 바로가기

Language/C++

[C++] 템플릿(Template) - 컴도리돌이

728x90

outline

intro to Generic Programming

Function Template

Class Template

STL

templates and inheritance


일반화 프로그래밍(generic Programming)

  • 일반화 프로그래밍은 알고리즘에서 매개변수로 제공되는 특정 유형(specific types)에 대해 필요할 때 인스턴스화(instantiated)하는 to-be-specified-later의 관점에서 작성된다.

  • C++에서 template는 서로 다른 타입으로 인해 반복적으로 작성해야 하는 코드를 피하기 위해 사용된다. 즉, 타입에 대해 일반화 접근(Generic Approach)을 한다.

  • 함수(functions)와 클래스(class)에서 template를 사용할 수 있다.

template <typename T>
void SelectionSort( T* array, int size) {
 for (int i = 0; i < size; ++i) {
     int min_idx = i;
     for (int j = i + 1; j < size; ++j) {
       if (array[min_idx] > array[j]) {
          min_idx = j;
       }
     }
 // Swap array[i] and array[min_idx].
   T tmp = array[i];
   array[i] = array[min_idx];
   array[min_idx] = tmp;
   }
}
  • 만약 위에 예시에 나오는 선택 정렬을 C언어에서 작성을 하였다면 타입만 다른 똑같은 코드를 필요할 때마다 여러 개 작성했어야 했다. 하지만 template로 새로운 변수 T를 통해서 타입에 따라 일반화 코드를 작성할 수 있다.

함수 템플릿(Function Template)

  • 함수를 작성할 때, 함수의 기능은 일반화가 되었지만, 자료형(type)에 대해서 모호하게 둔다.

  • 동일한 알고리즘의 기능을 다양한 자료형(type)에 대해 한 번 작성한다.

  • 특정 자료형(specific type)을 매개 변수(parameter)로 템플릿에 전달

template <typename T>

Swap function

template <typename T>
// naming the arbitrary type T. Programmers use simple names such as T .
void Swap(T &a, T &b) {
   T temp;
   temp = a;
   a = b;
   b = temp;
}
  • swap 함수를 작성할 때 인자로 받을 매개 변수의 자료형(type)을 template으로 받아서 자료형(type)에 따라 똑같은 swap함수를 만들 필요가 없어진다.

 

템플릿 인자 추론(Template argument deduction)

  • 컴파일러가 해당 템플릿 함수 호출의 사용 및 코드 내용에서 추론할 수 있는 템플릿 인수는 생략할 수 있다.
int i = 10;
int j = 20;
Swap<int>(i, j);
int i = 10;
int j = 20;
Swap(i, j);
  • 위에 예시처럼 변수 i와 j가 정수형(int)을 충분히 예상할 수 있기 때문에 Swap 함수에 template 자료형을 사용할 필요가 없다.

 

함수 템플릿 오버 로딩(Function Template Overloading)

  • 함수 템플릿도 동일한 함수 명을 사용하는 것을 허락한다. 다만 오버 로딩의 조건을 따라야 한다. (인자로 받는 변수의 개수를 다르게 하는 것을 추천)
template <typename T>
void Swap(T &a, T &b) {
   T temp;
   temp = a;
   a = b;
   b = temp;
}
template <typename T>
void Swap(T* a, T* b, int n) {
   T temp;
   for (int i = 0; i < n; i++)
    {
     temp = a[i];
     a[i] = b[i];
     b[i] = temp;
     };
}

클래스 템플릿(class template)

  • 클래스 멤버(class member)들도 template로 선언이 가능하다.

  • 멤버 함수를 클래스 외부에서 선언할 때, template 선언을 해주어야 한다.

  • 클래스 템플릿은 행당 클래스 객체를 생성할 때 template 타입을 선언해준다.

클래스 내부에 사용할 경우(Basic)

template <typename T>
// let the compiler know that you’re about to define a template
class Stack {
  private:
   enum { MAX = 10 }; // constant specific to class
   T items[MAX]; // holds stack items (type-independent)
   int top; // index for top stack item
public:
   Stack();
};
  • 함수 템플릿을 사용했던 것처럼 객체를 생성할 때만 template의 자료형(타입)을 넣어 주면 된다.

클래스 외부에 선언한 멤버 함수를 사용할 경우

template <typename T>
class MyPair {
     T a, b;
  public:
     MyPair(T first, T second) {
     a = first, b = second;
     }
     T get_max();
};
template <typename T>
T MyPair<T>::get_max() {
   T retval;
   retval = a > b? a : b;
   return retval;
}
int main() {
   int a_i = 100, b_i = 75;
   MyPair<int> my_pair_i(a_i, b_i);
   cout << "max(" << a_i << "," << b_i << ")=" << my_pair_i.get_max() << endl;

   double a_d = 1.5, b_d = -3.5;
   MyPair<double> my_pair_d(a_d, b_d);
   cout << "max(" << a_d << "," << b_d << ")=" << my_pair_d.get_max() << endl;

   return 0;
}
  • 멤버 함수를 사용할 때(위에 예시에서는 MyPair 함수를 사용할 때)마다 template의 자료형(타입)을 넣어준다.

멤버 함수에 있는 템플릿(Member Function Template)을 사용할 경우

  • 클래스 템플릿(Class template)의 매개 변수 이외의 부가적으로 템플릿 매개 변수를 제공하는 데 사용할 수 있음

  • 부가적으로 클래스 멤버 안에 템플릿을 사용할 경우 외부에 부가적으로 사용한 멤버 템플릿도 선언해줘야 한다.

template<typename T>
class X {
 public:
     template<typename U>
     void mf(const U& u);
};

template<typename T>
template<typename U>
void X<T>::mf(const U& u) {
 ...
}
int main() {
 ...
}

 

typename & class keyword

  • typename 은 'class'로 항상 대체될 수 있다.

template <class First, class Second>

struct Pair {
   First first;
   Second second;
   Pair(const First& f, const Second& s) : first(f), second(s) {}
};

template <class First, class Second>
Pair<First, Second> MakePair(const First& first,const Second& second) {
  return Pair<First, Second>(first, second);
}

int main (){
   Pair<int, int> p = MakePair(10, 10);
   // == MakePair<int, int>(10, 10);
   Pair<int, int> q = Pair<int, int>(20, 20);
   return 0;
}

 

비형식 템플릿 변수(Non-type Template Parameter)

  • c++ 템플릿에서는 비형식 매개 변수를 지원한다. ( 상수 정수 계열 값을 제공하여 배열의 길이를 지정할 수 있다.)

  • 변수는 constant로 표현되어야 한다.

  • const드로 취급되어야 한다.

template<class T, int size>
template<class T, int size>
class MyFilebuf {
   T* filepos;
   int array[size];
  public:
   MyFilebuf() { /* ... */ }
   ~MyFilebuf() {}
   ...
};

int main (){
 MyFilebuf<double, 200> x; // create object x of class
 MyFilebuf<double, 200.0> y;
 // error, 200.0 is a double, not an int
 return 0;
}
template <int i>
class C {
   public:
   int array[i];
   int k;
   C() { k = i; }
};

int main() {
   C<100> a; // can be instantiated
   C<200> b; // can be instantiated
   return 0;
}

표준 템플릿 라이브러리-STL(Standard Template Library)

  • STL은 템플릿(template)으로 작성된 제네릭 클래스와 함수 라이브러리이다.

1.순차(Sequence) : 연속적인 메모리 공간에 순차적으로 값을 저장하고 있는 컨테이너이다.

  • 벡터(Vectors) : 입력된 순서대로 저장

  • 리스트(List) : 순서가 있는 리스트 형태

  • 디큐(Deque - double-ended queue) : 양 끝단에서 입력과 출력이 가능

2.컨테이너 어댑터(Container adapter)

  • 스택(Stack) : 선입 후출(Last In Last Out)

  • 큐(Queue) : 선입선출(Frist In First Out)

3.연관 컨테이너(Associative Container : a generalization of sequence)

  • Set : key만을 저장하는 자료구조로 중복되지 않은 key를 저장한다. set에 값을 한번 넣어보는 것으로 중복된 값을 찾을 수 있다.

  • Map : key와 value 정보를 가지고 있는 자료구조이다.

  • Multimaps : key로 인덱싱 되는 여러 개의 다른 원소를 허용한다.


템플릿과 상속(Templates and Inheritance)

  • 일반 클래스가 파생 클래스인 경우 템플릿 클래스를 상속 가능하며, 템플릿 클래스가 파생 클래스인 경우에도 템플릿 클래스를 상속 가능하다.

template<class T>
class Base {
  public:
     void set(const T& val) { data = val; }
  private:
     T data;
  };
  
template<class T>
class Derived : public Base<T> {
// should be Base<T>, not just Base
  public:
   	void set(const T& val);
};

template<class T>
void Derived<T>::set(const T& v) {
	Base<T>::set(v);
 	// should be Base<T>, not just Base
}