C++ Template, Expression Templates #2

Short Articles 2012. 2. 17. 20:14
표현식 템플릿은 숫자 배열 클래스를 지원하기 위해 고안된 프로그램이 기술입니다
이번 아티클은 다음과 같이 진행합니다
1. 단순한 배열 클래스 구현하여 배열 연산에 대한 문제제기
2. 표현식 템플릿 구현
3. 표현식 템플릿을 아는 랩퍼클래스 구현
4. 실재 표현식 템플릿 사용해서 어떻게 동작하는 방법 추적

우리가 실질적으로 해야 하는 배열 연산입니다
배열을 어떻게 효율적으로 연산을 할까요?

이번에 사용되는 표현식을 살펴봅시다  1.2*x + x*y 
연산트리
연산트리로 표현하였습니다
여기에서 X,Y가 배열 클래스 입니다
1.2는 스칼라를 나타내는 클래스로 변경되고요
+,* 는 바로 표현식 템플릿이 되는 겁니다

전체 표현식을 다 읽기 전에는 표현식의 일부만
계산하지 않고 전체 계산하기전에 어떤 객체에
무슨 연산이 적용되었는지 기록하는 것

표현식 템플릿의 핵심입니다

 이 트리를 전위순회로 변경하면 다음과 같습니다




   이 트리를 전위순회로 변경하면 다음과 같습니다  


+, *, 1.2 가 클래스 템플릿으로 변경하고 주어진 표현식을 다음과 같은 데이터형을 가지는 객체로 바꿔봅시다

A_Add<  A_Mult<A_Scalar<double>, Array<double> >,
    A_Mult<Array<double>, Array<double> > >
새로운 배열 Array 클래스 템플릿과 A_Scalar, A_Add, A_Mult 와 함께 사용되었습니다

이제 본격적으로 알아봅시다
먼저 덧셈을 나타내는 A_Add 클래스입니다


여기서 중요한 것은 A_Add클래스는 배열의 덧셈연산을 나타내지만
실재 덧셈 연산을 하지 않습니다
다시 한번 언급하면 표현식 템플릿의 핵심 기술,  어떤 객체에 무슨 연산이 적용되었는지 기록하는 것 입니다
중요하니까 계속 반복할게요
여기 A_Add 클래스는 두 피연산자를 저장하고 덧셈 연산을 기록합니다
배열 참조 연산자에서 [] 덧셈을 기록하는 것입니다
두 피연산자를 저장했다가 A_Add 배열처럼 [] 접근할때 덧셈 연산을 한 결과를 제공합니다

그 다음은 곱셈을 나타내는 A_Mult 클래스 입니다 

곱셈도 마찬가지 입니다
다시 한번 언급하면 표현식 템플릿의 핵심 기술,  어떤 객체에
무슨 연산이 적용되었는지 기록하는 것 입니다
여기 A_Mult 클래스는 두 피연산자를 저장하고 곱셈 연산을 기록합니다
배열 참조 연산자에서 [] 곱셈을 기록하는 것입니다 
 다음을 스칼라를 나타내는 A_Scalar 클래스입니다

스칼라는 모든 인덱스에 대해 같은 값을 가지고 있는 배열과 같으므로 배열 참조 연산자 []에서는 모두 같은 값을 반환 합니다

도우미 클래스 A_Traits 클래스
피연산자를 정의할 때 이렇게 정의를 했습니다
typename A_Traits<OP1>::ExprRef op1;    // first operand
typename A_Traits<OP2>::ExprRef op2;    // second operand
최종 연산이 이루어지기 전까지 어떤 객체에 무슨 연산이 적용되었는지 기록하는 것
표현식 템플릿의 핵심입니다
대부분의 임시 노드들은 최상위 표현식과 관련이 있기에 전체 표현식 까지 살아 남아야 합니다
A_Scalar 노드는 예외 입니다
연산자 함수에 연결되며 전체 표현식의 계산이 종료 전에 사라집니다
(이게 왜그런지는 고민을 해보세요 그리고 저한테도 알려주세요 저도 잘 몰라서요) 
스칼리 피연산자는 값으로 복사돼야만 합니다

A_Traits 클래스 특질 클래스로 스칼라 처리를 하였습니다
A_Scalar, A_Add, A_Mult 이 세 클래스로 통해 표현식 템플릿을 표현합니다
앞으로 알아보기 힘들 정도로 템플릿의 향연을 보실 겁니다
그전에  짚고 넘어가겠습니다

A_Add는 두 피연산자를 저장하고 덧셈연산을 기록
A_Mult는 두 피연산자를 저장하고 곱셈연산을 기록
A_Scalar는 스칼라 값을 배열처럼 처리하는 것

템플릿 안에 템플릿이 가능하듯이 또는 곱셈 후에 덧셈, 덧셈 후에 곱셈... 연산후에 연산 하듯이 사용이 가능합니다
말을 복잡하게 썻네요 즉  A_Scalar, A_Add, A_Mult는 또 다른  A_Add, A_Mult의 피연산자로 가능합니다
그럴때는

A_Add 두 배열을 덧셈을 저장한 배열
A_Mult 두 배열을 곱셈을 저장한 배열 
A_Scalar 모두 같은 값을 같고 있는 배열
라고  판단하셔도 무관합니다
어짜피 시작을 배열연산에 발생하는 임시 변수의 비효율 때문에 템플릿 표현식을 언급했거든요 

너무 길어 졌네요
다음에는  실제 저장소를 제어하고 표현식 템플릿을 알고 있는 Array 형 클래스를 만들겠습니다
: