일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Effective c++
- operator new
- 영화 리뷰
- effective stl
- 참조자
- 비교 함수 객체
- more effective c++
- Vector
- 메타테이블
- 반복자
- 다형성
- 상속
- 오블완
- exception
- reference
- 언리얼
- 스마트 포인터
- resource management class
- 루아
- implicit conversion
- 암시적 변환
- Smart Pointer
- 영화
- c++
- 게임
- lua
- virtual function
- UE4
- 티스토리챌린지
- 예외
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 48. 템플릿 메타 프로그래밍 본문
728x90
항목 48. 템플릿 메타 프로그래밍, 하지 않겠는가?
템플릿 메타 프로그래밍(TMP)
- 컴파일 도중에 실행되는 템플릿 기반의 프로그램을 작성하는 일을 말한다.
- 템플릿 메타 프로그램은 C++ 컴파일러가 실행시키는, C++로 만들어진 프로그램이다.
템플릿 메타 프로그래밍(TMP)의 강점
- 다른 방법으로는 까다롭거나 불가능한 일을 굉장히 쉽게 할 수 있다.
- C++ 컴파일이 진행되는 동안에 실행되기 때문에, 기존 작업을 런타임에서 컴파일 타임으로 전환할 수 있다.
이를 통해 두 가지 이득을 취할 수 있는데,
- 일반적으로 프로그램 실행 도중에야 잡을 수 있었던 에러들을 컴파일 타임에 찾아낼 수 있다.
- TMP를 써서 만든 C++ 프로그램이 효율적일 여지가 높다.
- 컴파일 타임에 동작을 다 해가지고 오기 때문에 실행 코드가 작아지고, 실행 시간도 짧아지며, 메모리도 적게 잡아먹는다.
- 대신 컴파일 타임이 길어진다.
예시로 보는 TMP
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
// typeid 연산자를 사용한다.
if(typeid(typename std::iterator_traits<IterT>::iterator_category ==
typeid(std::random_access_iterator_tag))
{
iter += d;
}
else
{
if(d >= 0) { while(d--) ++iter; }
else { while (d++) --iter; }
}
}
- typeid 연산자를 쓰는 방법은 특성정보(traits)를 쓰는 방법보다 효율이 떨어진다.
- 타입 점검 동작이 컴파일 도중이 아니라 런타임에 일어나기 때문이다.
- 런타임 타입 점검을 수행하는 코드는 어쩔 수 없이 실행 파일에 들어가야 하기 때문이다.
- 그렇기에 특성정보 방법을 사용한다. (항목 47 참조)
- 이것이 바로 TMP이다.
- typeid 방법은 성능 외에 컴파일 문제를 일으킬 수도 있다.
typeid의 컴파일 문제
void advance(std::list<int>::iterator& iter, int d)
{
if(typeid(std::iterator_traits<std::list<int>::iterator>::iterator_category) ==
typeid(std::random_access_iterator_tag))
{
iter += d; // 이 코드 때문에
}
else
{
if(d >= 0) { while(d--) ++iter; }
else { while (d++) --iter; }
}
}
std::list<int>::iterator iter;
...
advance(iter, 10); // 이 경우 컴파일 에러가 발생한다!!
- list<int>::iterator는 양방향 반복자이므로 iter += d의 += 연산자는 지원하지 않는다.
- 컴파일 에러가 발생하는 이유는, 모든 소스 코드가 적법한지 검사하는 것이 컴파일러의 책무이기 때문이다.
튜링 완전성
- 범용 프로그래밍 언어처럼 어떤 것이든 계산할 수 있는 능력을 갖추고 있다.
- 변수 선언도 되고, 루프도 실행시킬 수 있고, 함수를 작성하고 호출할 수도 있다.
- TMP는 그 자체가 튜링 완전성을 가진다.
- 다만 보통의 C++ 구문요소들과 생김새가 많이 다르다.
TMP 구문요소의 생김새
- if..else 문
- 템플릿 및 템플릿 특수화 버전을 사용하여 처리한다.
- 루프 문
- 재귀(recursion)을 사용하여 루프의 효과를 낸다.
- TMP의 루프는 재귀 함수 호출을 만들지 않고 재귀식 템플릿 인스턴스화(recursive template instantiation)을 한다.
컴파일을 통해 계승(factorial)을 계산하는 템플릿
template<unsigned n>
struct Factorial
{
enum { value = n * Factorial<n-1>::value };
};
template<>
struct Factorial<0>
{
enum { value = 1; };
};
- 구조체 타입이 인스턴스화되도록 만들어져 있다.
- value라는 TMP 변수가 선언되어 있다. 이는 나열자 둔갑술(enum hack)이다. (항목 2 참조)
C++ 프로그래밍에서 TMP가 유용한 경우
- 치수 단위(dimensional unit)의 정확성 확인
- 예를 들면 속도를 나타내는 변수에 질량을 나타내는 변수를 대입하면 에러일 것이다.
- TMP를 사용하면 프로그램 안에서 쓰이는 모든 치수 단위의 조합이 제대로 됐는지를 컴파일 동안에 맞춰볼 수 있다.
- 즉, 선행 에러 탐지(early error detection)로 사용한다.
- 행렬 연산의 최적화
-
typedef SquareMatrix<double, 10000> BigMatrix; BigMatrix m1, m2, m3, m4, m5; ... // 일반적인 C++의 방식. // 이 경우 네 개의 임시 행렬이 생긴다. // 행렬 원소들 사이에 곱셈으로 인해 네 개의 루프가 만들어진다. BigMatrix result = m1 * m2 * m3 * m4 * m5;
- 표현식 템플릿(expression template)으로 개선
- 덩치 큰 임시 객체를 없애고 루프까지 합쳐버린다.
-
- 맞춤식 디자인 패턴 구현의 생성
- 전략, 감시자, 방문자 패턴 등의 디자인 패턴은 구현 방법이 여러 가지이다.
- 정책 기반 설계(policy-based design)을 사용한다.
- 따로따로 마련된 설계상의 선택(정책)을 나타내는 탬플릿을 만들어낼 수 있다.
- 이 정책 템플릿을 임의로 조합하여 패턴을 구현할 때 사용한다.
- 생성식 프로그래밍(generative programming)의 기초.
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 50. operator new / delete는 언제 커스텀해야 할까? (2) | 2024.07.24 |
---|---|
[Effective C++] 49. new 처리자 (6) | 2024.07.23 |
[Effective C++] 47. 특성 정보 클래스 (0) | 2024.07.19 |
[Effective C++] 46. 템플릿 클래스 안에 비멤버 함수 두기 (0) | 2024.07.18 |
[Effective C++] 45. 멤버 함수 템플릿으로 암시적 변환 지원 (1) | 2024.07.17 |
Comments