Programming Study/C++

템플릿_2

LeeWonTae 2014. 10. 14. 14:10

1. 템플릿_1 내용의 확장

Point 클래스 템플릿과 배열 클래스 템플릿

앞에서 우리는 Point 클래스를 다음의 형태로 템플릿화 하였다.

template<typename T>

class Point

{

private:

    T xpos, ypos;

public:

    Point(T x=0, T y=0);

    void ShowPosition() const;
};

그리고 다음의 형태로 BoundCheckArray 클래스 템플릿을 정의하였다.

template <typename T>

class BoundCheckArray

{

private:

    T * arr;

    int arrlen;

    BoundCheckArray(const BoundCheckArray& arr){  }

    BoundCheckArray& operator=(const BoundCheckArray& arr) {  }

public:

    BoundCheckArray(int len);

    T& operator[] (int idx);

    T operator[] (int idx) const;

    int GetArrLen() const;

    ~BoundCheckArray();

} ;

그렇다면, 위의 클래스 템플릿을 기반으로 Point<int> 템플릿 클래스의 객체를 저장할 수 있는 객체는 어떻게 생성해야 할까?

클래스 템플릿 기반의 객체생성에는 일정한 규칙이 존재하기 때문에, 이는 사실 어려운 문제가 아니다.

다음과 같이 객체를 생성하면,

BoundCheckArray<int> iarr(50); 

int형 데이터의 저장이 가능한 것처럼, 저장대상의 자료형이 Point<int>이니,

다음과 같이 객체를 생성하면, Point<int> 템플릿 클래스의 객체를 저장할 수 있다.

BoundCheckArray<Point<int>> oarr(50); 

그리고 저장대상이 Point<int> 템플릿 클래스의 객체가 아닌 Point<int>형 포인터라면, 다음과 같이 객체를 생성하면 된다.

BoundCheckArray<Point<int>*> oparr(50) ;

또한 위의 문장은 typedef 선언을 통해서 다음과 같이 구성할 수도 있다.

typedef Point<int>* POINT_PTR;

BoundCheckArray<POINT_PTR> oparr(50); 

 

템플릿 클래스라 하여 일반 클래스와 비교해서 다른 문법체계를 지니는 것은 아니니,

우리가 알고 있는 C++ 문법의 이해를 기반으로 템플릿과 관련된 다양한 확장이 가능하다.

 

특정 템플릿 클래스의 객체를 인자로 받는 일반함수의 정의와 friend 선언

Point<int>, Point<double>과 같은 템플릿 클래스의 자료형을 대상으로도 템플릿이 아닌 일반함수의 정의가 가능하고,

클래스 템플릿 내에서 이러한 함수를 대상으로 friend 선언도 가능하다.

 

 

2. 클래스 템플릿의 특수화(Class Template Specialization)

클래스 템플릿도 함수 템플릿처럼 특수화를 할 수 있다.

특수화의 방법 및 개념은 함수 템플릿과 매우 유사하다.

 

클래스 템플릿 특수화

함수 템플릿을 특수화하는 이유는 특정 자료형에 대해서 구분이 되는 다른 행동을 보이기 위해서이다.

마찬가지로 클래스 템플릿을 특수화하는 이유는 특정 자료형을 기반으로 생성된 객체에 대해,

구분이 되는 다른 행동양식을 적용하기 위해서이다.

즉, 클래스 템플릿을 특수화하면, 템플릿을 구성하는 멤버함수의 일부 또는 전부를 다르게 행동하도록 정의할 수 있다.

클래스 템플릿을 특수화 하는 방법은 다음과 같다.

먼저 다음과 같이 정의된 클래스 템플릿이 존재할 때,

template<typename T>

class SoSimple

{

public:

    T SimpleFunc(T num) { ..... }
};

이를 기반으로 자료형 int에 대해 특수화 한 템플릿 클래스는 다음과 같이 정의한다.

template <>

class SoSimple<int>

{

public:

    int SimpleFunc(int num) { ..... }
};

이렇게 int 형에 대해서 특수화가 되고 나면, 다음의 형태로 객체생성 시,

SoSimple<int> obj; 

특수화된 템플릿 클래스 SoSimple<int>를 대상으로 객체가 생성된다.

 

일부 컴파일러는 아직도 클래스 템플릿의 특수화를 지원하지 않거나, 지원하더라도 제한된 형태로만 지원하고 있다.

따라서 이러한 사실을 알고, 사용하는 컴파일러에서 컴파일 오류를 보이더라도 당황하지 않도록 하자.

 

클래스 템플릿의 부분 특수화

다음과 같이 정의된 클래스가 있다고 가정해 보자.

template <typename T1, typename T2>

class MySimple { ..... } 

여기서 T1과 T2를 각각 char와 int로 하여 특수화를 진행하면 다음의 형태가 된다.

template <>

class MySimple<char, int> { ..... } 

 템플릿의 정의에 사용된, 결정되지 않은 자료형의 수가 하나 더 늘어난 상황에서의 특수화를 보인것이다.

그렇다면 다음은 어떠한 의미를 지니는 정의인지 살펴보자

template <typename T1>

class MySimple<T1, int> { ..... }

이 역시 특수화의 결과이다.

단, T1과 T2 모두에 대해서 특수화를 진행한 것이 아니고, T2 하나에 대해서만 부분적으로 특수화를 진행한 것이다.

그래서 이를 가리켜 클래스 템플릿의 특수화(class template partial specialization)'이라 한다.

 

부분 특수화와 전체 특수화의 두 가지 모두에 해당하는 객체생성 문장에서는 전체 특수화된 클래스를 대상으로 객체가 생성된다.

전체 특수화가 부분 특수화보다 우선시 된다고 정리해두자.

 

 

3. 템플릿 인자

템플릿을 정의할 때 결정되지 않은 자료형을 의미하는 용도로 사용되는 T 또는 T1, T2와 같은 문자를 가리켜

'템플릿 매개변수'라 한다.

그리고 템플릿 매개변수에 전달되는 자료형 정보를 가리켜 '템플릿 인자'라 한다.

 

템플릿 매개변수에는 변수의 선언이 올 수 있습니다.

다음의 클래스 템플릿 정의를 보자.

이 정의에서 독특한 사실은 템플릿 매개변수의 선언에 마치 함수처럼 변수의 선언이 등장했다는 점이다.

template <typename T, int len>

class SimpleArray

{

private:

    T arr[len];

public:

    T& operator[] (int idx)

    {

        return arr[idx];

    }
} ;

이렇듯 템플릿 매개변수에도 변수가 올 수 있다.

그리고 이를 기반으로 다음의 형태로 객체생성이 가능하다.

SimpleArray<int, 5> i5arr;

SimpleArray<double, 7> d7arr; 

위의 두 문장에서 템플릿 매개변수 len에 전달된 인자 5와 7은 해당 템플릿 클래스에서 상수처럼 사용된다.

즉, len은 각각 5와 7로 치환되어, 컴파일러에 의해서 다음과 같이

SimpleArray<int, 5>형 템플릿 클래스와 SimpleArray<double, 7>형 템플릿 클래스가 각각 생성된다.

class SimpleArray<int, 5>

{

private:

    int arr[5];

public:

    int& operator[](int idx) { return arr[idx]; }
};

class SimpleArray<double, 7>

{

private:

    double arr[7];

public:

    double& operator[](int idx) { return arr[idx]; }
};

물론 위의 두 템플릿 클래스 SimpleArray<int, 5>와 SimpleArray<double, 7>은 서로 다른 자료형의 클래스로 구분된다.

 

템플릿 매개변수에 값을 전달받을 수 있는 변수를 선언하면,

변수에 전달되는 상수를 통해서 서로 다른 형의 클래스가 생성되게 할 수 있다.

 

 

템플릿 매개변수는 디폴트 값 지정도 가능합니다.

함수의 매개변수에도 디폴트 값의 지정이 가능하듯이, 템플릿 매개변수에도 디폴트 값의 지정이 가능하다.

템플릿 매개변수에 디폴트 값이 지정되어도, 템플릿 클래스의 객체생성을 의미하는 <>기호는 반드시 추가되어야 한다.

비록 그 안을 비워둘지라도 말이다.

 

 

4. 템플릿과 static

함수 템플릿과 static 지역변수

딱 한번 초기화된 상태에서 그 값을 계속 유지하는 static 변수의 특성을 잘 알고 있다고 가정하고 설명을 진행하겠다.

먼저 다음 함수 템플릿을 보자.

template <typename T>

void ShowStaticValue(void)

{

    static T num=0;

    num+=1;

    cout<<num<<" ";

함수 템플릿 내에 지역변수 num이 static으로 선언되었다.

그런데 위의 '함수 템플릿'을 기반으로 컴파일러는 다음과 같이 '템플릿 함수'들을 만들어 낸다.

void ShowStaticValue<int>(void)

{

    static int num = 0;

    num+=1;

    cout<<num<<" ";
}

 

void ShowStaticValue<long>(void)

{

    static long num=0;

    num+=1;

    cout<<num<<" ";
}

따라서 static 지역변수도 템플릿 함수 별로 각각 존재하게 된다.

 

클래스 템플릿과 static 멤버변수

static 멤버변수는 변수가 선언된 클래스의 객체간 공유가 가능한 변수이다.

따라서 다음과 같이 클래스 템플릿이 정의되면,

template <typename T>

class SimpleStaticMem

{

private:

    static T mem;

public:

    void AddMem(int num) { mem+=num; }

    void showMem() { cout<<mem<<endl; }
} ;

 

template <typename T>

T SimpleStaticMem<T>::mem = 0;   // 이는 템플릿 기반의 static멤버 초기화 문장이다.

컴파일러에 의해서 다음과 같이 템플릿 클래스들이 생성되어,

class SimpleStaticMem<int>

{

private:

    static int mem;

public:

    void AddMem(int num) { mem+=num; }

    void ShowMem() { cout<<mem<<endl; }
} ;

 

int SimpleStaticMem<int>::mem=0;

 

class SimpleStaticMem<double>

{

private:

    static double mem;

public:

    void AddMem(double num) { mem+=num; }

    void ShowMem() { cout<<mem<<endl; }
} ;

 

double SimpleStaticMem<double>::mem=0;

템플릿 클래스 별로 static 멤버변수를 유지하게 된다.

 

언제 template<typename T>를 쓰고 언제 template<>를 쓰는가?

정의 부분에 T가 존재하면 T에 대한 설명을 위해서 <typename T>의 형태를 덧붙이면 되고,

T가 존재하지 않으면<>의 형태로 간단하게 선언하면 된다.

 

템플릿 static 멤버변수 초기화의 특수화

template <typename T>

T SimpleStaticMem<T>::mem=0; 

이렇게 선언하면 SimpleStaticMem<int>의 mem=0, SimpleStaticMem<double>의 mem=0으로 초기화된다.

그렇다면 SimpleStaticMem<double>의 mem을 0이 아닌 5로 초기화 하는 방법은 없을까?

물론 있다! 위의 초기화문에 대해서 특수화를 진행하면 된다.

아래처럼 말이다.

template <>

long SimpleStaticMem<long>::mem=5; 

 

 

 

 

- 윤성우 저, 열혈강의 C++ 프로그래밍 中 -