* 정의
템플릿 메타 프로그래밍(template meta programming, TMP)은 컴파일 도중에 실행되는 템플릿 기반의 프로그램을 작성하는 것을 말한다. 템플릿 기반 프로그램을 작성함으로써 컴파일러에게 프로그램 코드를 생성하도록 하여 컴파일 시점에 많은 것들을 결정하도록 만든다.
일반적인 프로그래밍 방식에서 실행 시점에서 대부분 수행한다면 템플릿 메타 프로그래밍 방식에서는 컴파일 시점에 많은 것들을 수행하여 실행 시점에서의 연산을 줄인다.
템플릿 메타 프로그램은 변수의 값이 한번 정해지면 변경할 수 없기 때문에 함수형 프로그래밍의 한 형태로 볼 수 있다. 많은 템플릿 구현에서 흐름 제어가 재귀를 통해 수행된다. 유사한 코드 중복을 피하는 일반화 프로그래밍과도 연관있다.
* 장점
템플릿 메타 프로그래밍의 장점, 템플릿의 장점은 다음과 같이 정리할 수 있다.
- 컴파일 시점에 연산을 하기 때문에 실행 시점에서 연산을 하거나 함수를 별도로 호출하는 시간을 아낄 수 있다. 또한 컴파일 시간에 확인하기 때문에 문제점을 더 빠르게 파악할 수 있다.
- 상수전파(constant propagation)이 더 잘 일어날 수 있다.
여기서 상수전파란 컴파일러의 실행시간 최적화 기법으로 컴파일 시 상수를 포함하는 연산이 계산될 수 있으면 계산하두어 코드를 줄이는 방식이다. 다음과 같은 코드가 있다고 가정할 때 y의 값은 컴파일 시 6으로 계산된다.
x = 3;
y = 2 * x;
- 정적 다형성을 구현할 수 있다.
동적 다형성(가상 함수)은 알맞은 가상함수를 호출하기 위해 vtable을 확인해서 적절한 함수를 호출하게 되며 이는 오버헤드로 이어진다. 정적 다형성을 구현하면 더 성능이 좋다. 일반적인 경우에는 부모 클래스에서 자식 클래스의 정보를 모르지만 템플릿을 활용하면 가능하다. 다음과 같이 구현된다.
template <typename Super>
class Base
{
public:
void doSomething(void)
{
// ...
static_cast<Super*>(this)->doSomethingImpl();
// ...
}
};
class Derived : public Base<Derived>
{
public:
void doSomethingImpl(void)
{
// ...
}
};
- 상수만 사용될 수 있는 문맥에 템플릿 정보를 활용할 수 있다. 예를 들어 배열의 원소 개수를 지정할 때는 무조건 상수의 값을 받을 수 있는데, 이에 템플릿을 사용할 수 있다. 이를 활용하면 힙에 할당하지 않고 스택에서 처리할 수 있다.
// std::array
template <class _Ty, size_t _Size>
class array { // fixed size array of values
public:
// ...
_Ty _Elems[_Size];
};
- 내부 자료형이 확정되지 않아도 클래스나 함수를 만들어내는 것이 가능하다. 일반화 프로그래밍(generic programming)에 특화되어 있다고 보면 된다.
- 가변인자를 전달할 수 있으며 일반 가변 함수 매개변수나 매크로와 달리 타입 안정성을 갖출 수 있다.
https://create-new-worlds.tistory.com/303
가변인자 템플릿(Variadic Template) 예시
* printf 일반 가변인자 함수와 비교했을 때 가변인자 템플릿은 타입 안정성을 갖출 수 있다. 일반적으로 우리가 사용하는 printf의 경우 %d와 같이 어떤 형식으로 출력해야할 지 정해줘야 한다. 하
create-new-worlds.tistory.com
- 암시적 자료 변환을 막기 위해 컨디션에 따라 특정 타입을 선언하는 std::enable_if와 같은 함수를 활용할 수 있다.
std::enable_if는 조건에 따라 해당 타입을 정의할지 정의하지 않을 지 결정하는 기법으로 조건에 맞지 않는다면 컴파일 에러를 발생시키게 된다. 사용 예시는 다음과 같다.
// 정수형 타입이 들어왔을 때만 반환형인 bool이 정상적으로 정의된다.
template <typename T>
typename std::enable_if<std::is_integral<std::remove_reference_t<T>>::value, bool>::type
is_odd(T&& i) { return bool(i % 2); }
// 두번째 템플릿 인자는 조건 체크를 위해 넣어둔 것이다. 이를 숨기기 위한 두가지 방법이 존재한다.
// 1. 뒤에 = 0을 붙인다. 2. 앞에 typename = 을 붙인다.
// typename = 방식
template<typename T, typename = typename std::enable_if<std::is_integral<std::remove_reference_t<T>>::value, void>::type>
bool is_even(T&& i) { return !bool(i % 2); }
// = 0 방식
template<typename T, typename std::enable_if<std::is_integral<std::remove_reference_t<T>>::value, int>::type = 0>
bool is_even1(T&& i) { return !bool(i % 2); }
* 단점
- 컴파일 시점에 연산해야하는 것들이 많아서 컴파일 시간이 늘어난다.
- 특정 소스파일 내부에서만 사용하는 것이 아니라면 항상 헤더 파일에 있어야 한다.
- 통상적인 반복문 대신 재귀 호출로 구현해야 한다.
- 일반 코드에 비해 가독성이 떨어진다.
- 디버깅하기 매우 어렵다. 오류 메시지를 보고 판단해야하는데 오류 메시지도 템플릿과 연관되어 있기 때문에 너무 많고 복잡하다.
- 배포 시 다른 개발자들에게 내부 코드를 강제로 공개하게 된다.
- 컴파일러마다 템플릿 사용 방법이 조금씩 다를 수 있어서 이식성에 문제가 있을 수 있다.
* 예시
https://create-new-worlds.tistory.com/307
템플릿 메타 프로그래밍(Template Meta Programming, TMP) 예시
* pow template struct MyPow { static const long long result = m * MyPow ::result; }; template struct MyPow { static const long long result = 1; }; * factorial template struct MyFactorial { static c..
create-new-worlds.tistory.com
'C++' 카테고리의 다른 글
| 템플릿 메타 프로그래밍(Template Meta Programming, TMP) 예시 (0) | 2022.05.24 |
|---|---|
| 가변인자 템플릿(Variadic Template) 예시 (0) | 2022.05.23 |
| 가변인자 템플릿(Variadic Template) (0) | 2022.05.23 |
| 템플릿(Template) (0) | 2022.05.23 |
| 람다(Lambda) (0) | 2022.04.11 |