C++/Templates

C++ Templates

Soul-Learner 2016. 12. 26. 15:10

C++ 프로그래밍, 템플릿 ( Templates )


템플릿(Templates)의 사전적인 의미

  • Template. 型板, 견본, 본보기. 무언가를 만들기 위해서 시초가 되는 양식
  • 鑄型(거푸집): 만들려는 물건의 모양대로 속이 비어 있어 거기에 쇠붙이를 녹여 붓도록 되어 있는 틀
  • 거푸집을 일단 한개 만들어 놓으면 여러번 사용할 수 있으므로 얼마든지 동일한 물건을 다수개 생산할 수 있다
  • 거푸집은 실제 기능할 수 있는 물건은 아니지만 실제 사용가능한 물건을 대량으로 생산할 수 있는 틀이다


C++ 언어에서 템플릿의 의미

C++ 언어에서는 함수나 클래스를 정의할 때 그 함수나 클래스가 처리할 데이터의 자료형을 구체화하지 않고 일반화하여 선언하는 템플릿을 정의할 수 있다. 템플릿을 선언한 후에 실제 해당 함수나 클래스를 사용할 때는 처리할 자료형을 구체적으로 지정하는 절차를 거쳐야 사용할 수 있다. 함수 템플릿이나 클래스 템플릿은 실제의 인스턴스는 아니므로 사용할 수는 없다. 그러므로 개발자가 템플릿의 자료형을 구체화하는 코드를 작성하면 컴파일러는 템플릿을 참조하여 자료형을 구체적으로 지정하여 실제 함수나 클래스를 컴파일 타임에 생성하여 컴파일한다.


C++ 함수 템플릿의 선언 형식

  • template <class 식별자>         함수 선언;  // 자료형이 다수개일 때는 단순히 반복한다 <class 식별자, class 식별자, ...>
  • template <typename 식별자>  함수 선언;

위의 템플릿 선언 형식에서 2개의 형식이 class를 사용하는 경우와 typename을 사용하는 경우로 나뉘는데, 이는 완전히 동일한 기능을 한다

template <class T> // 자료형은 나중에 결정할 것이므로 템플릿에서는 임시 자료형으로 T를 사용하여 내용을 완성한다

void exchange(T& a, T& b) {

T tmp = a;

a = b;

b = tmp;

}

함수 템플릿을 이용하면 아래와 같이 호출부에서 함수를 쉽게 생성할 수 있으며 이 때 구체적인 자료형을 결정해야 한다

exchange<char>(a, b); // 컴파일 타임에 자료형이 char 인 exchange함수가 생성되고 런타임에 호출된다


함수 템플릿을 사용하는 예

#include <iostream>
#include <locale>

using namespace std;

// 함수 템플릿 선언
template <class T>
void exchange(T& a, T& b) {
	T tmp = a;
	a = b;
	b = tmp;
}

int main() {
    setlocale(LC_ALL, "");

    wcout << L"C++ 함수 템플릿 (Templates)" << endl;

    char a = 'a';
    char b = 'b';

    // 템플릿을 이용한 함수 생성(컴파일 탐임) 및 호출(런타임)
    exchange<char>(a, b);

    wprintf(L"a=%c, b=%c \n", a,b);

    return 0;
}


클래스 템플릿 ( Class Templates )

클래스는 멤버변수, 멤버함수 그리고 생성자 등으로 구성되므로 이들에게 임시 자료형을 선언하면 클래스 템플릿이 된다. 함수 템플릿과 동일하게 클래스 선언에 앞서 템플릿 선언문이 있어야 한다

#include <iostream>
#include <locale>

using namespace std;

// 클래스 템플릿 선언
template <class T>
class MyUtil {
    public:
    T a, b;

    public:
    MyUtil(T a, T b) {
        this->a = a;
        this->b = b;
    }

    void exchange() {
        T tmp = a;
        a = b;
        b = tmp;
    }
};

int main() {
    setlocale(LC_ALL, "");

    wcout << L"C++ 함수 템플릿 (Templates)" << endl;

    // 클래스 템플릿을 이용한 클래스 생성 및 객체생성
    MyUtil<int> util(100,200);
    wprintf(L"a=%d, b=%d \n", util.a, util.b);

    MyUtil<double> util2(1.2, 5.3);
    wprintf(L"a=%f, b=%f \n", util2.a, util2.b);

    return 0;
}

위의 예에서는 클래스 내부에 함수가 정의되는 인라인 함수만 예로 들었는데, 인라인 함수가 아니라면 클래스 외부에 멤버 함수를 정의해야 하는데 임시 자료형을 사용하는 멤버함수를 클래스 외부에 정의할 때는 그 형식이 좀더 까다로운 편이다. 

외부에 정의되는 생성자와 멤버함수가 임시자료형을 사용한다면 클래스 선언시 사용했던 template 선언문을 다시 선언해야 하고  클래스명 오른쪽에 임시 자료형을 붙여서 생성자나 멤버함수를 정의하면 된다

#include <iostream>
#include <locale>

using namespace std;

// 클래스 템플릿 선언
template <class T>
class MyUtil {
	public:
	T a, b;

	public:
	MyUtil(T, T);

	void exchange();
};

// 템플릿 클래스의 생성자 정의. 클래스에도 임시 자료형을 붙여서 선언한다
template <class T>
MyUtil<T>::MyUtil(T a, T b) {
	this->a = a;
	this->b = b;
}

// 템플릿 클래스의 멤버함수 정의. 클래스명에 임시 자료형을 붙인다
template <class T>
void MyUtil<T>::exchange() {
	T tmp = a;
	a = b;
	b = tmp;
}

int main() {
    setlocale(LC_ALL, "");

    wcout << L"C++ 함수 템플릿 (Templates)" << endl;

    // 클래스 템플릿을 이용한 클래스 생성 및 객체생성
    MyUtil<int> util(100,200);
    wprintf(L"a=%d, b=%d \n", util.a, util.b);

    MyUtil<double> util2(1.2, 5.3);
    wprintf(L"a=%f, b=%f \n", util2.a, util2.b);

    return 0;
}


템플릿 특수화 ( Template Specialization )

템플릿을 통해  함수나 클래스가 처리할 자료형만을 변경하여 컴파일 타임에 해당 함수나 클래스를 생성하는 것이 템플릿의 개념이므로 자료형만 다를 뿐이지 처리 내용은 획일적으로 동일한 것이다. 특별한 상황에서는 특정 자료형만은 템플릿의 획일적인 처리를 적용하지 않고 특별한 처리과정을 적용해야 하는 경우도 있을 것이다. 이런 경우에 템플릿 특수화를 적용하여 특정 자료형에만 적용되는 템플릿을 별도로 선언해야 한다.

일반 클래스 템플릿과 클래스 템플릿 특수화 선언의 비교

  • template <class T> class MyUtil             { ... };       // 일반 클래스 템플릿
  • template <>          class MyUtil<string> { ... };       // 클래스 템플릿 특수화

클래스 템플릿 특수화를 정의할 때는 일반 클래스 템플릿에 정의된 모든 멤버는 그대로 템플릿 특수화에도 다시 정의해 주어야 한다


템플릿을 선언할 때 임시 자료형 뿐만 아니라 결정된 자료형을 사용하여 템플릿을 정의할 수도 있고 디폴트 값도 선언할 수 있다

template <class T=string, int N=10> class MyUtil {..};


헤더파일과 소스파일을 분리할 때 템플릿을 사용하고 있다면?

템플릿 선언과 템플릿의 정의는 한 파일에 모두 있어야 한다