템플릿을 통해서 타입이 마치 인자 인것 처럼 사용하는 것
템플릿 인자로는 타입 뿐만이 아니라 특정한 조건을 만족하는 값들도 올 수 있음.
Array<int, 5> Array<int, 3>
위의 두 개 클래스는 다른 클래스임. , 다른 템플릿 인자로 인스턴스화 되었기 때문! 컴파일러는 Array<int, 5> 와 Array<int, 3> 를 위해 각기 다른 코드를 생성하며 다른 클래스의 객체들을 만들어요
#include <iostream>
#include <typeinfo>
template <int N>
struct Int {
static const int num = N;
};
template <typename T, typename U>
struct add {
typedef Int<T::num + U::num> result;
};
int main() {
// one 타입과 two 타입은 1과 2의 값을 나타내는 타입
typedef Int<1> one;
typedef Int<2> two;
typedef add<one, two>::result three;
std::cout << "Addtion result : " << three::num << std::endl;
}

template <typename T, typename U>
struct add {
typedef Int<T::num + U::num> result;
};
위 add 클래스의 템플릿은 인자로 두 개의 타입을 받아서 그 타입의 num 멤버를 더해서 새로운 타입인 result 를 만들어 냄.
typedef add<one, two>::result three;
실제 덧셈을 수행하는 부분. add 클래스를 함수라고 생각한다면 그 계산 결과를 내부 result 타입으로 반환하는 것!
three라고 부름three::num을 3으로 치환함.값을 부여할 수 있고, 또 그 타입들을 가지고 연산 을 할 수 있음.컴파일 타임에 생성되는 코드로 프로그래밍을 하는 것 = 메타 프로그래밍(meta programming)/* 컴파일 타임 팩토리얼 계산 */
#include <iostream>
template <int N>
struct Factorial {
static const int result = N * Factorial<N - 1>::result;
};
template <>
struct Factorial<1> {
static const int result = 1;
};
int main() {
std::cout << "6! = 1*2*3*4*5*6 = " << Factorial<6>::result << std::endl;
}
// 실행 결과
// 6! = 1*2*3*4*5*6 = 720
template <>
struct Factorial<1> {
static const int result = 1;
};
위의 구문을 통해 Factorial<1> 타입의 경우만 따로 result=1로 만들어주어 재귀적 구조가 끝날 수 있게 해줌. 출력 결과인 720은, 단순히 컴파일러가 만들어낸 Factorial<6> 이라는 type을 나타내고 있을 뿐임.
but,
따라서 TMP 를 이용하는 경우는 꽤나 제한적이지만, 많은 C++ 라이브러리들이 TMP 를 이용해서 구현되었음. (Boost 라이브러리) 또한, TMP 를 통해서 컴파일 타임에 여러 오류들을 잡아낼 수 도 있고 (Ex. 단위나 통화 일치 여부등등) 속도가 매우 중요한 프로그램의 경우 TMP 를 통해서 런타임 속도도 향상 시킬 수 있음.
int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
두 수의 최대공약수를 구하기 위한 유클리드 호제법
#include <iostream>
template <int X, int Y>
struct GCD {
static const int value = GCD<Y, X % Y>::value;
};
template <int X>
struct GCD<X, 0> {
static const int value = X;
};
int main() {
std::cout << "gcd (36, 24) :: " << GCD<36, 24>::value << std::endl;
}
위의 코드를 TMP로 수정
typedef Ratio_add<rat, rat2> rat3;
using rat3 = Ratio_add<rat, rat2>;
typedef 대신 위와 같이 using 이라는 키워드를 사용할 수 있음.
typedef void (*func)(int, int);
using func = void (*)(int, int);
함수 포인터의 경우 만일 void 를 리턴하고 int, int 를 인자로 받는 함수의 포인터의 타입을 func 라고 정의하기 위해서는 typedef 로 첫번째 줄과 같이 작성했어야 했지만, 이를 using을 사용하면 직관적으로 나타낼 수 있음.
- auto 키워드auto는 초기화 값에 따라 알아서 데이터 타입을 정해주는 키워드
auto 키워드는 템플릿의 사용으로 복잡해진 타입 이름들을 간단하게 나타낼 수 있는 획기적인 방법. 짧은 이름의 타입일 경우 그냥 써주는 것이 좋지만, 복잡한 타입 이름의 경우, 그 타입을 쉽게 추측할 수 있다면 auto 키워드를 활용하는 것도 좋음.
#include <iostream>
#include <typeinfo>
int sum(int a, int b) { return a + b; }
class SomeClass {
int data;
public:
SomeClass(int d) : data(d) {}
SomeClass(const SomeClass& s) : data(s.data) {}
};
int main() {
auto c = sum(1, 2); // 함수 리턴 타입으로 부터 int 라고 추측 가능
auto num = 1.0 + 2.0; // double 로 추측 가능!
SomeClass some(10);
auto some2 = some;
auto some3(10); // SomeClass 객체를 만들까요?
std::cout << "c 의 타입은? :: " << typeid(c).name() << std::endl;
std::cout << "num 의 타입은? :: " << typeid(num).name() << std::endl;
std::cout << "some2 의 타입은? :: " << typeid(some2).name() << std::endl;
std::cout << "some3 의 타입은? :: " << typeid(some3).name() << std::endl;
}

컴파일러는 최대한 단순한 방법으로 추론하기 때문에 auto some3(10); 를 int 변수로 만들었음.