rlo-lo 2024. 5. 22. 19:55

템플릿이란?

 

: 함수나 클래스를 개별적으로 작성하지 않아도, 여러 자료형으로 사용할 수 있도록 만들어 놓은 틀

  • 함수 템플릿
  • 클래스 템플릿

- 컴파일 시간에 ~

- 객체의 자료형을 인수로 처리함

 


 

1. Function Template 

  • 함수를 만들어낼 때, 함수의 기능은 명확하지만, 자료형을 모호하게 두는 것.

- 템플릿으로 사용하는 변수가 2개 이상일 경우, 함수이름 뒤에 <>안에 명확하게 사용하지 않는다.

→ 컴파일러가 스스로 자료형 판단 

- template 선언 시 <typename T>, <class T> 둘 다 사용 가능 

 

 

기본 문법

template <typename T>
T functionName (T parameter1, Tparameter2, ...) {
	// code
    }
    
a = functionName<int> (4,6); 
b = functionName<double> (4.4, 6.6);

 

 

Different return Type 

 

 


 

 

2. Class Template

 

기본 문법

template <typename T>
class className {
private:
 T var;
public:
 T functionName (T arg);
};

 

- 오버로딩과 다르게 '다른' 함수로 취급된다. 

- 디폴트 템플릿 인자를 명시할 수 있음. template<typename T = int > 는 디폴트 템플릿 인자가 int 임

- 기초 클래스로 상속할 수 있음. 

- 함수 템플릿을 호출할 때와 달리, 클래스 템플릿을 사용할 때는 반드시 타입을 명시해야 함. 

≫ 클래스의 생성자가 호출되기 이전에 멤버 변수의 타입을 확인해, 객체의 메모리 공간을 할당해야 할 필요가 있기 때문이다. 

 

 

 

non-type parameter

 

 

#include <iostream>
using namespace std;
template <typename T>//class Array 정의 
//template<typename T, int arraySize>//arraySize는 멤버변수처럼 사용될 수 있음 

//template은 템플릿 데이터 타입 + 어떠한 primitive data types을 받을 수 있음 !
class Array {
public:
	explicit Array(int size) : arraySize{ size } {
		ptr = new T[arraySize];//배열을 가리키는 포인터 
	}
	int getSize() const {
		return arraySize * sizeof(T);
	}
	~Array() {
		delete[] ptr;
	}
private:
	T* ptr = nullptr;
	int arraySize = 0;
};
int main() {
	Array<int> arr1{ 5 };//Array<int>타입의 객체 생성 
	//Array <int, 5>arr1; 
	cout << arr1.getSize() << endl;
	Array<double> arr2{ 5 };
	cout << arr2.getSize() << endl;
	return 0;
}

 

 

Type Inference

 

 


 

3. Specialization

- 함수 템플릿과 클래스 템플릿는 특정 데이터 타입으로 그것의 버전을 정의할 수 있다. 

- 컴파일러는 템플릿 버전을 우선적으로 체크한 후, 메인 템플릿을 체크한다. 

- 함수 템플릿 -> 명시적 특수화 가능, 부분 특수화 불가능

  ※ 오버로딩으로 해결할 수 있음. 명시적 특수화된 함수보다 위에 있을 것!!

- 클래스 템플릿 -> 명시적 특수화, 부분 특수화 가능

순서 : 일반함수 > 템플릿 특수화 함수 > 템플릿 알맞은 함수

 

  • 완전 특수화 → 템플릿 매개 변수들을 전부 다 특수화 함
  • 부분 특수화 → 템플릿 매개 변수들이 여러개일 때 그 중 일부만 특수화 함
    • 전역 함수의 경우 문제 없이 가능
    • 멤버 함수의 경우 클래스 외부에선 부분 특수화 불가능
      • 클래스 내부에서만 가능하므로 상속을 이용함

 

template <typename T>
class className {
 T functionName (T arg);
};
template <>
class className <int> {
 int functionName (int arg);
};

 

 

/ Example. char * Specializtion /

#include <iostream>
using namespace std;

template <typename T>//main
T add(T a, T b) {
	return a + b;
}

template<>//specialization
const char* add(const char* a, const char* b){
	return "char*"; 
}

int main() {
	cout << add(1, 2) << endl; 
	cout << add("rlo", "lo") << endl; 
}

/* 출력
3
rlo lo*/

 

- 인자 수 다를 수 있음

- 특수화 템플릿에  기본 parameter 표시 x (main template 승계)

 

/ Example . 클래스 명시적 특수화

#include <iostream>
using namespace std;
 
// 클래스 템플릿
template <typename T>
class MyClass {
private:
	T x;
public:
	MyClass(T _x);
	T Caldouble();
};
 
// double 형에 대한 명시적 특수화
template <>
class MyClass<double> {
private:
	double x;
public:
	MyClass(double _x) { x = _x; }
	double Caldouble() {
		cout << "double 형 데이터를 2배하여 출력합니다. " << endl;
		return x * 2;
	};
};

 

 

 

/ Example. 클래스 부분적 특수화 

// 3개의 인자를 가지는 클래스 템플릿 예
 
// 일반적인 클래스 템플릿
template <typename A, typename B, typename C>
class MyClass {};
 
// 첫 번째 인자에 대한 특수화 - A가 int일 경우
template <typename B, typename C>
class MyClass<int> {};
 
// 첫 번째, 세 번째 인자에 대한 특수화 - A가 int, C가 double일 경우
template <typename B>
class MyClass<int, B, double> {};
 
// 모든 인자에 대한 특수화 - A가 int, B가 int, C가 double일 경우
template <>
class MyClass<int, int, double> {};

 

 

 

 

//Example. const char* 

#include <iostream>
#include <exception>
#include<stdexcept>
using namespace std;


template <typename T>
class MyData {
public:
	explicit MyData(T value) : data{ value } {}
	T getData() const { return data;  }
private: 
	T data; 

};

template <>
class MyData<char*> {
public:
	explicit MyData(const char* value) : data{ new char[strlen(value)] } {
		strcpy_s(data, strlen(value)+1, value);//널문자 포함 목적지 버퍼의 크기 전달
	}
	const char* getData() const { return data; }
	~MyData() { delete[] data; }
private:
	char* data;
}; 

int main() {
	cout << MyData{ 5 }.getData() << endl;
	cout << MyData<char*>{"dlwlrma"}.getData() << endl;
}