사용자가 원하는 타입을 지정하면 해당 타입 형태의 물건이 나옵니다.
데이터 형식이나 함수의 일반적인 모양을 정의하는데 사용됩니다.
템플릿은 코드의 재사용성을 증가시킵니다.
일반적으로 컴파일 타임에 타입 안정성을 보장합니다.
#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;
}
템플릿 인자에 기본 값을 넣어서 사용하는 것도 가능합니다.
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;
}
템플릿 인자에 기본 값을 넣어서 사용하는 것도 가능합니다.
#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++ 컴파일러는 함수를 컴파일 시에, 자신의 앞에 정의되어 있는 함수들 밖에 보지 못하기 떄문입니다.
템플릿 함수는 작성 시 그 순서에 유의해서 써야 합니다.
template <int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0> {
static const int value = 1;
};
템플릿을 사용하여 컴파일 타임에 계산되는 값을 생성할 수 있습니다.
템플릿 명시적 특수화에 대해서 예시를 들고 설명해주세요.
디폴트 템플릿 인자에 대해서 설명하고 예시를 보여주세요.
가변 길이 템플릿에 대해서 예시를 들고 설명해주세요.
N 번째 피보나치 수를 나타내는 TMP 를 만들어보세요. 참고로 피보나치 수는, N 번째 항이 N - 1 번째 항과 N - 2 번째 항의 합으로 정의되는 수 입니다. 참고로 1, 1, 2, 3, 5, ... 로 진행됩니다.
TMP 를 사용해서 어떤 수가 소수인지 아닌지를 판별하는 프로그램을 만들어보세요.
템플릿 메타 프로그래밍은 깊이 공부하면 거의 다른 언어수준마냥 끝이 없더라구요
글 잘 봤습니다!