모두의 코드 내용을 공부하고 정리한 내용입니다.



template

  • 클래스 탬플릿에 인자를 전달해서 실제 코드를 생성하는 것을 클래스 템플릿 인스턴스화라 한다.


  • 인스턴스화 되지 않는다면 컴파일시 아무런 코드로 변환하지 않는다.
    • 인스턴스화가 되어야만 컴파일러가 실제 코드를 생성한다.


  • 클래스 템플릿과 달리 함수 템플릿를 호출할 때<> 없이 컴파일이 된다 인수의 타입을 보고 알아서 <> 작동시켜 인스턴스화를 해준다.
    • 전달되는 인자를 보고 컴파일러가 타입을 정한다.



템플릿 특수화

  • 어떤 특정한 상황에서 별도로 작업을 수행하기 위해 특별히 따로 템플릿을 만들어줌
template <typename A, typename B, typename C> 
class test {}; 
  • 여기서 A가 int형이고 C가 double형일 때 다른 방식으로 처리하고 싶다면
template <typename B>
class test<int, B, double> {};
  • 위 처럼 특수화를 시키기 특수화 하려는 타입을 전달하고 위에는 일반적인 템플릿을 받으면 된다.
    • 만약 템플릿 인자가 없더라도 특수화를 하고 싶으면 template<>빈칸 이라도 남겨줘야한다



포인터에 대한 템플릿 특수화

  • 포인터로 구체화할 때 특수하게 간접 참조 값을 출력하고 싶을 때 사용
    • 포인터 타입들로 구체화할 때 다르게 동작
template <typename T>
class A
{
  private:
    T data;
  public:
    A(const T& create) : data(create) {}

    void print()
    {
      cout << data << endl;
    }
};

template <typename T>
class A<T*>
{
  private:
    T* data;
  public:
    A(T* create) : data(create) {}

    void print()
    {
      cout << *data << endl;
    }
};

int main()
{
  A<int> a_int(777); 
  a_int.print();

  int tmp = 369;
  A<int*> a_int_ptr(&tmp); // 포인터에 대한 템플릿 특수화를 사용하지 않으면 주소값이 출력됨됨
  a_int_ptr.print();
}
  • class A<T*>T*에 대한 특수화여서 모든 타입의 포인터 타입에 대한 특수화를 가르킨다.
    • 따라서 T를 생략하면 안되기에 제대로 명시해줘야 한다.



타입이 아닌 템플릿 인자 (non-type template arguments)

  • 템플릿 인자로 타입만 받을 수 있는 것은 아니다.
template <typename T, int num>
T add_num(T t) {
  return t + num;
}
int main() {
  int x = 3;
  std::cout << "x : " << add_num<int, 5>(x) << std::endl;
}
  • template 인자로 T를 받고, 추가적으로 함수의 인자처럼 int num을 또 받고 있다.


  • 타입이 아닌 템플릿 인자로 전달할 수 있는 타입들은 제한되어있다.여기 참조
    • 정수 타입
    • 포인터 타입
    • enum 타입
    • 널포인터터



디폴트 템플릿 인자

  • 함수에 디폴트 인자를 지정하는 것 처럼 템플릿도 디폴트 인자를 지정할 수 있다.
template <typename T, int num = 5>  // num에 디폴트로 5가 전달된다.
T add_num(T t) {
  return t + num;
}
int main() {
  int x = 3;
  std::cout << "x : " << add_num(x) << std::endl;
}


  • 또한 타입도 디폴트로 지정이 가능하다.
template <typename T, typename C = MyTemplateClass<T>>
T Func(T a, T b){ ... }
  • C에 디폴드 인자로 MyTemplateClass<T>가 전달된다.



가변 길이 템플릿

  • 임의의 개수의 인자를 받는 함수를 만들 수 있다.
template <typename T>
void print(T arg) {
  std::cout << arg << std::endl;
}
template <typename T, typename... Types>
void print(T arg, Types... args) {
  std::cout << arg << ", ";
  print(args...);
}
int main() {
  print(1, 3.1, "abc");
  print(1, 2, 3, 4, 5, 6, 7);
}
  • typename 뒤에 ...으로 오는 것을 parameter pack이라고 한다.
    • 템플릿 파라미터 팩, 함수 파라미터 팩
    • 0개 이상의 템플릿 인자와 0개 이상의 함수 인자를 표현
      • 차이점은 템플릿 경우 타입 앞에 ...이 오고, 함수의 경우 타입 뒤에 ...이 온다.
    • 위 print 템플릿 함수 예시로 printf(1, 2.2, "3"); 을 호출하면 인자가 여러개여서 가변 길이 템플릿 함수가 호출되고,
    • T에는 첫 번째 매개변수인 1의 int타입 추론되고 arg는 1이 된다.
    • 그리고 나머지는 뒤의 args가 된다.
      • 그 후 재귀적으로 다시 함수가 호출되면서 args값이 전달된다.


  • 만약 인자가 1개이면 c++규칙상 파라미터 팩이 없는 함수의 우선순위가 더 높아서 평범한 함수가 호출된다.

여기서 print 함수의 위치를 바꾸면 컴파일 오류가 발생!!


template <typename T, typename... Types>
void print(T arg, Types... args) {
  std::cout << arg << ", ";
  print(args...);
}
template <typename T>
void print(T arg) {
  std::cout << arg << std::endl;
}
int main() {
  print(1, 3.1, "abc");
  print(1, 2, 3, 4, 5, 6, 7);
}
  • 이유는 C++ 컴파일러는 함수를 컴파일 시 자신의 앞에 정의되어 있는 함수들 밖에 보지 못하기 때문이다.
    • 그래서 void print(T arg, Types... args)를 컴파일할 때 void print(T arg) 함수의 존재를 모른다.


  • 하지만 이런 가변 길이 템플릿은 아주 편리하지만 1가지 단점인 재귀 함수 형태로 구성해야 한다는 점이다.
    • 재귀를 종료시킬 함수를 따로 만들어 줘야한다.
      • 귀찮고 복잡해짐


  • C++17에 생긴 Fold형식을 사용하면 재귀형식을 사용하지 않고 간단하게 표현할 수 있다.



Fold expressions

template <typename... Ints>
int sum_all(Ints... nums) {
return (... + nums);
}
  • return (... + nums); 이것이 Fold형식으로, 컴파일은 return ((((1 + 4) + 2) + 3) + 10);으로 해석한다.
    • 위 형태를 단항 좌측 Fold (Unary left fold)라고 한다.


  • C++17에는 4가지 형태의 Fold 표현식이 존재한다. init는 초기값 pack은 파라미터 펙으로 전달된 인자.


imgae 자세한 내용

  • op에는 이항 연산자들이 들어가고 1가지 중요한 점은 ()로 감싸주지 않으면 컴파일 오류가 난다.


  • 위 표의 4번째, 즉 이항 좌측 Fold의 예를 알아보면
template <typename Int, typename... Ints>
Int diff_from(Int start, Ints... nums) {
  return (start - ... - nums);
}
  • start가 초기값이고 nums가 파라미터 팩 부분이다.



타입이 아닌 템플릿 인자로 전달할 수 있는 타입

참조

참조

fold


개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!

카테고리:

업데이트:

댓글남기기