본문 바로가기

Dev/C++

template

generic #

template은 C++의 generic한 기능을 받쳐주는 핵심적인 도구입니다.

이 도구에 의해 탄생한 STL과 표준 라이브러리들은
확장성과 타입 안정성등의 이점으로 다른 언어의 라이브러리들과는
다른 진화된 디자인을 보여주고 있습니다.

이름 그대로 template은 타입에대한 명세서와 같습니다.

template 자체가 타입이 아니고 타입에 대한 내용을 기술함으로서
타입생성에 틀이 되는 것입니다.

붕어빵을 예를들면 붕어빵에 팥을 넣느냐 아니면 계란을 넣느냐에 따라
붕어모양의 붕어빵이 나오느냐 붕어모양의 계란빵이 나오느냐와
같은 이치입니다.


그 붕어빵 틀이 template 입니다.


template #

#include <string>
using std::string;

template <class T>
T Add(T tp1, T tp2)
{
        return tp1 + tp2;
}

int main()
{
        int n = Add(5, 2);
        string str1 = "C";
        string str2 = "++";
        string str = Add(str1, str2);
}

위에 코드에서 Add는 두 가지 타입에 대해 사용되었습니다.
한개는 int형이며 한개는 std::string 형입니다.

컴파러는 위의 코드를 컴파일 할때
template <class T>
T Add(T tp1, T tp2)
{
        return tp1 + tp2;
}
이 코드를 틀로 하여 두개의 함수를 만들어 낼것입니다.

int Add(int tp1, int tp2)
{
        return tp1 + tp2;
}

std::string Add(std::string tp1, std::string tp2)
{
        return tp1 + tp2;
}
실제로 컴파일러의 컴파일작업은 위의 두개의 생성된 함수를 이용하여
컴파일을 합니다.

만약 + 연산자를 제공하지 않는 타입이나 객체를
Add 함수에 인수로 제공을 한다면 컴파일러는 에러 메세지를 출력 할것 입니다.

class NoOperator {};

int main()
{
        NoOperator Op1;
        NoOperator Op2;

        // 에러입니다. 
        // NoOperator는 operator+() 제공하지 않습니다. 
        Add(Op1, Op2);
}
이것이 타입 안정성의 단적인 예를 보여준것 입니다.
적법하지 않는 객체나 타입은 컴파일 타임에 에러를 발생시켜
런타임상의 에러를 막아줄수 있습니다.


클래스도 위의 함수와 같이 적용됩니다.

#include <string>
using std::string;

template <class T>
class TWrapper
{
public:
        T& GetRefValue()
        {
                return tp_;
        }

private:
        T tp_;
};


int main()
{
        TWrapper<int> Int;
        TWrapper<string> String;
}

위의 int와 string 타입의 TWrapper 템플릿은 다음과 같이
코드가 생성 될것입니다.


<int 버전>
class TWrapper
{
public:
        int& GetRefValue()
        {
                return tp_;
        }

private:
        int tp_;
};

<string 버전>
class TWrapper
{
public:
        string& GetRefValue()
        {
                return tp_;
        }

private:
        string tp_;
};
물론 TWrapper에 대한 두개의 같은 이름은 컴파일러에 의해
적법하게 치환될것 입니다.



template specialization #

템플릿 전문화는 template이 특정 타입에 특정한 행동을 할수 있도록
만들어 놓은 것입니다.

template <class T>
T Add(T tp1, T tp2)
{
        return tp1 + tp2;
}

int main()
{
        const char *sz = Add("C", "++");
}
위의 Add는 const char*와 매치가 될것 입니다.

const char* Add(const char* tp1, const char* tp2)
{
        return tp1 + tp2;
}
우선 const char* 가 const 타입이란 점에서 +연산을 수행할수 없으며
어떻해든 강제로라로 +연산을 수행하더라도 char* 타입의 +연산은 아무런
의미가 없는 것이고 원하는 결과도 아닐것 입니다.

이럴때 const char* 에 대해 전용 Add함수를 만들오 주고 싶은 욕망이
생길수도 있을겁니다.

이럴때 사용하는것이 template specialization입니다.

template <class T>
T Add(T tp1, T tp2)
{
        return tp1 + tp2;
}

template <>
const char* Add(const char* p1, const char* p2)
{
        static char sz[1024] = {0, };
        strcpy(sz, p1);
        strcat(sz, p2);
        return sz;
}


int main()
{
        // template Add 함수 버전을 사용합니다. 
        int n = Add(3, 2);

        // const char* 특화버전 Add 함수를 사용합니다. 
        const char* p = Add("C", "++");
}

클래스의 전문화도 같이 적용되며 특정타입을 사용못하도록 하는
버전을 만들어 보겠습니다.

template <class T>
class TWrapper
{
public:
        T& GetRefValue()
        {
                return tp_;
        }

private:
        T tp_;
};


// const char* 타입에 대해서는 인스턴스를 못만들게 하기위해 
// 아래와 같은 템플릿 클래스의 전문화를 만듭니다. 
template <>
class TWrapper<const char*>
{
private:
        TWrapper() {}; // private 생성자입니다. 
};


int main()
{
        // 성공입니다. 
        TWrapper<int> Int;

        // 에러입니다. 
        // 위의 const char* 의 클래스전문화가 
        // const char* 인스턴스생성을 막아줍니다. 
        // 기특하기도 하쥐 -_- 
        TWrapper<const char*> CharP;
}

Members of Class Templates #

클래스(템플릿클래스도 포함)의 멤버함수도 템플릿이 될수 있습니다..

class Adder
{
public:
        template <class T>
        T Add(const T& t1, const T& t2)
        {
                return t1 + t2;
        }
};


int main()
{
        Adder adder;
        int n = adder.Add(3, 4);
        double d = adder.Add(5.3, 2.3);
}

template partial specialization #

특정 타입의 행동에 대해 정의 하는 템플릿전문화와 달리
부분 템플릿 전문화는 템플릿 인수 T에 대해 포인터형이나 참조형과
같은 형의 부분적인 타입의 특성을 전문화를 할때 사용됩니다.

#include <iostream>
#include <string>
using namespace std;


template <class T>
class Any
{
public:
        string GetType()
        {
                return "class Any";
        }
};


template <class T>
class Any<T *>
{
public:
        string GetType()
        {
                return "class Any<T *>";
        }
};

template <class T>
class Any<const T>
{
public:
        string GetType()
        {
                return "class Any<const T>";
        }
};


template <class T>
class Any<const T*>
{
public:
        string GetType()
        {
                return "class Any<const T*>";
        }
};


int main()
{
        Any<int> A1;
        cout << A1.GetType() << endl;

        Any<int *> A2;
        cout << A2.GetType() << endl;

        Any<const int> A4;
        cout << A4.GetType() << endl;

        Any<const int *> A5;
        cout << A5.GetType() << endl;
}

결과:
class Any
class Any<T *>
class Any<const T>
class Any<const T*>

'Dev > C++' 카테고리의 다른 글

Conversion Functions - 변환함수  (0) 2008.05.01
explicit  (0) 2008.05.01
template  (0) 2008.05.01
functor - 함수객체  (0) 2008.05.01
Casting operators  (0) 2008.05.01
export  (0) 2008.05.01

태그