C++ 템플릿

NYH·2023년 12월 9일

C++

목록 보기
13/17

목차

  1. 템플릿
  2. 함수 템플릿
  3. 클래스 템플릿
  4. 가변 길이 템플릿
  5. 템플릿 메타 프로그래밍
  6. 문제풀이


1. 템플릿

템플릿이란?

사용자가 원하는 타입을 지정하면 해당 타입 형태의 물건이 나옵니다.
데이터 형식이나 함수의 일반적인 모양을 정의하는데 사용됩니다.
템플릿은 코드의 재사용성을 증가시킵니다.
일반적으로 컴파일 타임에 타입 안정성을 보장합니다.


2. 함수 템플릿

함수 템플릿 예시

#include <iostream>
#include <string>

template <typename T>
T max(T& a, T& b) {
  return a > b ? a : b;
}

int main() {
  int a = 1, b = 2;
  std::cout << "Max (" << a << "," << b << ") ? : " << max(a, b) << std::endl;

  std::string s = "hello", t = "world";
  std::cout << "Max (" << s << "," << t << ") ? : " << max(s, t) << std::endl;
}


함수 템플릿의 인스턴스화

template <typename T>
void Swap(T& a, T& b)
{
	T temp;
	temp = a;
	a = b;
	b = temp;	
}

int main()
{
	int x = 10;
    int y = 20;
    
    // int 타입 인스턴스 생성됨.
    Swap(x, y);
}

함수 템플릿이 각각의 타입에 대해 처음으로 호출될 때 C++ 컴파일러는 해당 타입의 인스턴스를 생성합니다.
이렇게 생성된 인스턴스는 해당 타입에 대해 특수화된 템플릿 함수입니다.
이 인스턴스는 함수 템플릿에 해당 타입이 사용될 때마다 호출됩니다.


명시적 특수화

// 원형
template <typename T>
void Swap(T& a, T& b);
template <typename T> void Swap(T& a, T& b);

// double형 
template <> void Swap<double>(double&, double&) { ... };

// 원형
template <typename A, typename B, typename C>
void test(A a, B b, C c)

// 명시적 특수화
template <typename B>
void test(int a, B b, double c)

다음과 같이 특정한 타입을 정의하여 사용하는 것을 명시적 특수화라고 합니다.


타입이 아닌 템플릿 인자

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

템플릿 인자로 타입만 받는 것이 아니라 다음과 같이 다른 인자를 템플릿에 넣는 것도 가능합니다.


디폴트 템플릿 인자

#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(x) << std::endl;
}

템플릿 인자에 기본 값을 넣어서 사용하는 것도 가능합니다.


3. 클래스 템플릿

  • 예시
template <typename T>
class SampleTemplate
{
public:
	T getData() const;
	void setData(T value);	
}


명시적 특수화

// 일반 type A, type B, type C일때 동작
template <typename A, typename B, typename C>
class test {};

// A가 int이고, type B, C가 duoble일때 동작
template <typename B>
class test<int, B, double> {};

세가지 타입을 받는 원형과, A가 int, C가 double인 경우에만 작동하는 특수한 경우를 제작할 수 있습니다. 이러한 경우를 템플릿 특수화라고 합니다.

template <>
class Vector<bool> {
  ...  // 원하는 코드
}

유의점은 전달하는 템플릿 인자가 없더라도 특수화 하려면 template<> 라도 남겨줘야 합니다.


타입이 아닌 템플릿 인자

#include <iostream>

template <typename T, int Size>
class Array {
public:
    Array() {
        for (int i = 0; i < Size; ++i) {
            data[i] = T();  // 기본값으로 초기화
        }
    }

    void Print() const {
        for (int i = 0; i < Size; ++i) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }

    void Fill(const T& value) {
        for (int i = 0; i < Size; ++i) {
            data[i] = value;
        }
    }

private:
    T data[Size];
};

int main() {
    // int 타입의 배열
    Array<int, 5> intArray;
    intArray.Print();

    // double 타입의 배열
    Array<double, 3> doubleArray;
    doubleArray.Print();

    // 문자열 배열
    Array<std::string, 4> stringArray;
    stringArray.Fill("Hello");
    stringArray.Print();

    return 0;
}

템플릿 인자로 타입만 받는 것이 아니라 다음과 같이 다른 인자를 템플릿에 넣는 것도 가능합니다.


디폴트 템플릿 인자

#include <iostream>

template <typename T = int, int Size = 5>
class Array {
public:
    Array() {
        for (int i = 0; i < Size; ++i) {
            data[i] = T();  // 기본값으로 초기화
        }
    }

    void Print() const {
        for (int i = 0; i < Size; ++i) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }

    void Fill(const T& value) {
        for (int i = 0; i < Size; ++i) {
            data[i] = value;
        }
    }

private:
    T data[Size];
};

int main() {
    // 디폴트 템플릿 인자 사용
    Array<> defaultIntArray;
    defaultIntArray.Print();

    // 디폴트 타입 사용, 크기는 3
    Array<double, 3> doubleArray;
    doubleArray.Print();

    // 디폴트 크기 사용, 타입은 std::string
    Array<std::string> stringArray;
    stringArray.Fill("Hello");
    stringArray.Print();

    return 0;
}

템플릿 인자에 기본 값을 넣어서 사용하는 것도 가능합니다.


4. 가변 길이 템플릿

예시

#include <iostream>

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

Types... args type name 뒤에 …으로 오는 것을 템플릿 파라미터 팩(parameter pack) 이라고 부릅니다.
함수에 인자로 …로 오는 것은 함수 파라미터 팩이라고 부르며, 0개 이상의 함수 인자를 나타냅니다.
둘의 차이점은 템플릿의 경우 타입 앞에 ... 이 오고 함수의 경우 타입 뒤에 ... 이 옵니다.

print(1, 3.1, "abc");

--------------------------

template <typename T, typename... Types>
void print(T arg, Types... args) {
  std::cout << arg << ", ";
  print(args...);
}

여기서 타입 T 에는 int 로 추론되고 args…에는 남은 3.1 과 abc 가 들어갑니다.
그리고 아래와 같이 유추 됩니다.

void print(int arg, double arg2, const char* arg3)
{
		std::cout << arg << ", ";
	  print(arg2, arg3);	
}

-----------------------------------------
void print(double arg, const char* arg2)
{
		std::cout << arg << ", ";
	  print(arg2);	
}

-----------------------------------------
void print(const char* arg) {
  std::cout << arg << std::endl;
}

위의 함수는 print를 재귀적으로 반복할 것입니다.


주의사항

#include <iostream>

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

print 함수의 위치를 변경하면 위의 가변 길이 템플릿에서는 아래 print 함수의 위치를 찾을 수가 없습니다.
그 이유는 C++ 컴파일러는 함수를 컴파일 시에, 자신의 앞에 정의되어 있는 함수들 밖에 보지 못하기 떄문입니다.
템플릿 함수는 작성 시 그 순서에 유의해서 써야 합니다.


5. 템플릿 메타 프로그래밍

template <int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static const int value = 1;
};

템플릿을 사용하여 컴파일 타임에 계산되는 값을 생성할 수 있습니다.


6. 문제 풀이

문제 1.

템플릿 명시적 특수화에 대해서 예시를 들고 설명해주세요.


문제 2.

디폴트 템플릿 인자에 대해서 설명하고 예시를 보여주세요.


문제 3.

가변 길이 템플릿에 대해서 예시를 들고 설명해주세요.


문제 4.

N 번째 피보나치 수를 나타내는 TMP 를 만들어보세요. 참고로 피보나치 수는, N 번째 항이 N - 1 번째 항과 N - 2 번째 항의 합으로 정의되는 수 입니다. 참고로 1, 1, 2, 3, 5, ... 로 진행됩니다.


문제 5.

TMP 를 사용해서 어떤 수가 소수인지 아닌지를 판별하는 프로그램을 만들어보세요.

profile
그냥 해라

2개의 댓글

comment-user-thumbnail
2023년 12월 9일

템플릿 메타 프로그래밍은 깊이 공부하면 거의 다른 언어수준마냥 끝이 없더라구요
글 잘 봤습니다!

1개의 답글