Expression Template(표현식 템플릿, ET)

9671 단어 template
1. 앞말
이전 글스스로 간단한string류를 실현하다에서 + 조작부호 재부팅 함수를 실현할 때 되돌아올 때 생성된 임시 대상이 복사 구조 함수를 호출하여 메모리 공간을 동적 신청하는 것을 방지하기 위해 모브라는 함수를 사용했습니다. 이것은 C++0x에 추가된 기능입니다.C++0x가 추가한 특성인 만큼 이전에 이런 특성이 없었던 상황에서 임시 대상이 메모리 공간을 동적 신청하는 문제는 다른 방법으로 해결하거나 피할 수 있지 않을까요?정답은 긍정적이며 Expression Template(표현식 템플릿, ET)로 해결할 수 있습니다.
2. 표현식 템플릿
이전 스트링 클래스의 경우 다음 표현식을 자주 사용할 수 있습니다.
  String str4 = str1 + str2 + str3;
이 표현식에 문제가 하나 있는데, 바로 '불필요한' 임시 대상이 생겼다는 것이다.str1 +str2의 결과는 임시 대상temp1에 저장되고,temp1 +str3의 결과는 임시 대상temp2에 저장되며,temp2는 마지막으로str4에 결과를 전송하여 초기화합니다.만약 이러한 벡터가 매우 길거나 표현식에 몇 마디를 더하면 많은 임시 대상이 생길 뿐만 아니라, 스트링 내부의 문자열을 저장할 메모리 공간을 동적으로 신청해야 하기 때문에 이것은 분명히 저효과적이다.move 함수는 + 조작부호 재부팅 함수가 임시 대상을 되돌릴 때 컴파일러가 창고에서 다른 대상을 복사하는 비용을 해결합니다. (+ 조작부호 재부팅 함수에 명시된 임시 대상temp1은 함수의 역할 영역에서 사용할 수 없기 때문에 컴파일러가 자동으로 + 조작부호 재부팅 함수를 호출하는 영역에서 임시 대상temp11을 명시합니다. 복사된 temp1을 저장합니다.)그러나 임시 대상이 생기는 문제는 해결되지 않았다.표현식 템플릿의 사고방식은 매우 간단하다. 바로 + 조작에 대해 계산을 미루는 것이다. 즉, 모든 + 조작을 한꺼번에 계산하면 임시 대상이 + 조작의 임시 결과를 저장할 필요가 없다.계산을 미루려면,str1,str2,str3 작업수를 저장해야 합니다. 나중에 실제 계산이 지연된 + 작업에 사용됩니다.
3. String 기반 표현식 템플릿 구현
표현식 템플릿의 사고방식은 매우 간단하지만 사실은 생각보다 그렇게 간단하지 않다.원래의 방법에서operator+는 직접 계산을 진행했다. 기왕 우리가 너무 이르게 계산하고 싶지 않다면 우리는 반드시operator+연산자를 다시 불러와야 한다. 이 연산에서 진정한 연산을 하지 않고 하나의 대상을 생성하고 이 대상에서 덧셈 연산자 양쪽의 조작수를 보류한 다음에 다음 계산에 참여하도록 해야 한다.(그래, 이 대상도 임시적이지만, 그 대가는 매우 적다. 왜냐하면 동적 메모리 공간을 신청할 필요가 없기 때문에 우리는 우선 그것을 상관하지 않는다).
class String;

template <typename L>
class ExpPlus {
    const L &lstr;
    const char *rstr;
    const int _size;
public:
    ExpPlus(const L  & a_l, const char *a_r):lstr(a_l), rstr(a_r), _size(strlen(a_r))
    {
    }
    ExpPlus(const L  & a_l, const char &a_r):lstr(a_l), rstr(&a_r), _size(1)
    {
    }
    ExpPlus(const L  & a_l, const String &a_r):lstr(a_l), rstr(a_r.c_str()), _size(a_r.size())
    {
    }
    void copy_str(char *d_str) const;
    int size() const
    {
        return _size + lstr.size();
    }
};

template <typename L>
void ExpPlus<L>::copy_str(char *d_str) const
{
    lstr.copy_str(d_str);
    strncpy(d_str + lstr.size(), rstr, _size);
}
// + 
template <typename L>
ExpPlus<L> operator + (const L & a_l, const char  & a_r) {
    return ExpPlus<L>(a_l, a_r);
}

template <typename L>
ExpPlus<L> operator + (const L & a_l, const char*  a_r) {
    return ExpPlus<L>(a_l, a_r);
}

template <typename L>
ExpPlus<L> operator + (const L & a_l, const String  & a_r) {
    return ExpPlus<L>(a_l, a_r);
}

우리는 덧셈 계산의 '표현식' 을 나타내는 템플릿 종류인 ExpPlus를 추가했지만, 덧셈을 할 때 그 자체가 진정한 계산을 하지 않았다.이 종류에 대한copy 정의str 함수, 이 함수에서야 진정한 문자열 덧셈 계산을 진행하였다.물론 String 객체 저장 외에도 char 및 char* 저장도 지원합니다.String 객체에 문자열 상수와 문자를 더하는 것도 다음 표현식과 같이 일반적이기 때문입니다.
  String str2 = str1 + 'a' + "cd"
우리가 실행한String 클래스에 대해operator = 함수를 다시 불러와야 오른쪽 표현식의 값을 한꺼번에 계산할 수 있습니다.수정된 String 코드는 다음과 같습니다.
class String
{
public:
    。。。
    template <typename Exp>
    String& operator=(const Exp &a_r); // 
    。。。
    void copy_str(char *d_str) const; // 
};

void String::copy_str(char *d_str) const
{
    strcpy(d_str, _string);
}

template <typename Exp>
String& String::operator=(const Exp &a_r)
{
    char *temp_str;
    int size = a_r.size();

    temp_str = new char[size + 1];
    a_r.copy_str(temp_str);
    temp_str[size] = 0;
    if (_string)
        delete _string;
    _string = temp_str;
    _size = size;
    return *this;
}

위에서 새로 추가된 코드만 제시했고 다른 코드는 스스로 간단한string류를 실현하다에서 이미 제시했다.
위에서 말한 바와 같이ET를 잘 모르는 사람에게는 아직 이해하기 어려울 수도 있습니다. 한 걸음 한 걸음 해 봅시다.
str4 =str1 +str2 +str3 이 식에서 먼저str1 +str2를 만나면 템플릿 함수operator +가 호출됩니다. 이때 임시 Exp플러스 대상만 생성합니다. (우리는 t1이라고 부릅니다) 계산을 하지 않고 계산의 좌우 조작수(즉str1과str2)만 보류합니다. 이어서 t1 +str3은 같은operator +를 다시 호출합니다.그리고 하나의 대상만 생성한다(우리는 그것을 t2라고 부른다). 이 대상의 유형은 ExpPlus이다. 마찬가지로 t2는 여기에 양쪽의 조작수(즉 t1과str3)만 보존한다.전체 표현식이'완성'될 때까지 어떤 것도 계산을 하지 않았고 하는 일은 사실상 Exp플러스라는 템플릿 클래스로 계산식의 정보를 기록했을 뿐이다(물론 이 정보들은 계산에 참여하는 조작수이다).
마지막으로str4=t2를 진행할 때 String의 값 연산자가 호출됩니다. (t2를 매개 변수로 합니다.)주의, 이 호출 중인 문장 ar.copy_str(temp str), 실제 t2. 호출copy_str(temp_str),t2.copy_str 함수에서 t1.copy_str, t1 또str1.copy_str, 이렇게 한 걸음 한 걸음 str1, str2, str3의 문자열을temp 로 복사합니다str에서 최종적으로str4=str1+str2+str3을 얻는다.'매직'이 됐듯 ExpPlus를 통해'딜레이 컴퓨팅'을 마쳤고, 대형 스트링 임시 대상이 생기지 않도록 했다.
4. 요약
표현식 템플릿은 표현식의 직관성과 효율을 유지하는데 매우 강하지만 너무 복잡하다. 주로 라이브러리의 디자이너로서의 무기이다.또한 사용자로 하여금'새로운'것들을 이해하게 할 수도 있다. 예를 들어 표현식의 중간값을 저장하고 싶다면 ...> 사용자들에게 반나절 동안 이해하게 할 것이다.
 
참조: http://www.cnblogs.com/liyiwen/archive/2009/12/03/1616627.html
http://www.cppblog.com/kesalin/archive/2009/05/28/85983.html

좋은 웹페이지 즐겨찾기