본문 바로가기
프로그래밍 언어/C++

C++ 문법 / 템플릿

by Nighthom 2023. 2. 13.

1. 개요

// 함수 템플릿
template <typename T>
T add(T a, T b) {
    return a + b;
}

// 클래스 템플릿
template <typename T>
class Number {
public:
    T getNum() { return num; }	
private:
    T num;
}

템플릿은 형틀에 무언가를 넣고 찍어내는 것처럼 소스 코드를 찍어낼 수 있게 해준다. 위의 예시 코드에서 사용되는 add함수를 int형 변수 a, b로 호출한다고 치면 컴파일러는 해당 함수를 아래와 같이 바꾸어서 컴파일한다. 

int add(int a, int b);

 

 

클래스의 경우도 똑같은 로직을 사용해서 컴파일하게 된다.

 

2. 템플릿 특수화

만약 템플릿을 사용하면서 특수한 자료형에 대해서 템플릿 특수화하고 싶다면 다음과 같이 사용한다.  

template <typename A, typename B, typename C>
class A{};
// 위 클래스를 특수화하고 싶다면(B에 int, C에는 double이 오도록 하고싶을 경우)
template <typename A>
class A<A, int, double> {
// 구현 로직
}
// 과 같은 꼴로 나타낸다.

3. 타입이 아닌 템플릿 인자

#include <iostream>

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;
}

위와 같은 소스 코드를 컴파일하면 8이 출력되게 된다. 

int add_num(int t) {  // T는 int로 치환
    return t + 5;         //  num은 컴파일 타임에 상수로 치환
}

컴파일 타임에 위 소스코드로 치환되었기 때문에 이런 식으로 발생하게 되는 것. 

 

위와 같은 타입이 아닌 템플릿 인자로 사용할 수 있는 것은 다음과 같다.

  • 정수 타입들 (bool, char, int, long 등등). 당연히 float  double 은 제외
  • 포인터 타입
  • enum 타입
  • std::nullptr_t (널 포인터)

4. 디폴트 템플릿 인자

타입 없는 템플릿 인자에 디폴트 값을 정의해 줄 수 있다. 

#include <iostream>

template <typename T, int num=5>
T add_num(T t) {
  return t + num;
}

int main() {
  int x = 3;
  std::cout << "x : " << add_num<int>(x) << std::endl;
}

이러면 디폴트 인자인 5를 활용해서 대입해준다.

4. 가변 길이 템플릿 인자

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 << std::endl;
  print(args...);
}

템플릿은 가변 길이 인자 또한 받을 수 있다.

typename... Types는 0개 이상의 템플릿 인자를 의미한다. (이와 같은 것을 템플릿 파라미터 팩이라고 부른다)

파라미터에서의 Types... 또한 0개 이상의 파라미터를 의미한다. (이와 같은 것을 함수 파라미터 팩이라고 부른다)

args...는 Types에 해당하는 모든 정보를 전달하게 된다.

 

이러한 구조는 마치 재귀 함수가 호출되듯 인자가 하나씩 줄어드는 모습을 볼 수가 있는데, 이러한 재귀의 종료를 위해서 인자가 하나일 경우의 함수 또한 정의해 주어야 한다.

 

댓글