[C++] 클래스 템플릿(class template)

bolee·2022년 12월 14일
0

C++

목록 보기
16/16
post-thumbnail

클래스 템플릿(class template)

C++에서 클래스 템플릿(class template)이란 클래스의 일반화된 선언을 의미한다.

클래스 템플릿을 사용하면, 타입에 따라 다르게 동작하는 클래스 집합을 만들 수 있다.
즉, 클래스 템플릿에 전달되는 템플릿 인수(template argument)에 따라 별도의 클래스를 만들 수 있게 된다.
템플시 인수는 타입이거나 명시된 타입의 상수값일 수 있다.

클래스 템플릿 문법

클래스 템플릿은 다음과 같은 문법으로 정의할 수 있다.

template <typename 타입 이름>
class 클래스 템플릿 이름
{
	// ...
}

C++98에서 추가된 typename 키워드는 이전에 class 키워드를 사용했기 때문에 템플릿 정의 내에서 typename 키워드 대신에 class 키워드를 사용할 수 있다.


클래스 템플릿 예제

#include <iostream>

tmplate <typename T>
class Data
{
private:
	T data_;
public:
	Data(T dt);
    T get_data();
}

int main()
{
	Data<string> str_data("클래스 템플릿");
    Data<int> int_data(12);
    
    std::cout << "str_data : " << str_data.get_data() << std::endl;
    std::cout << "int_data : " << int_data.get_data() << std::endl;
    return 0;
}

template <typename T>
Data<T>::Data(T dt)
{
	data_ = dt;
}

template <typename T>
T Data<T>::get_data()
{
	return data_;
}

Output

str_data : 클래스 템플릿
int_data : 12

클래스 템플릿의 특징

클래스 템플릿은 다음과 같은 특징을 가진다.

  1. 하나 이상의 템플릿 인수를 가지는 클래스 템플릿을 선언할 수 있다.
    // 두 개의 템플릿 인수를 가지는 클래스 템플릿 선언
    template <typename T, int i>
    class X
    { ... }
  2. 클래스 템플릿에 디폴트 템플릿 인수를 명시할 수 있다.
    // 디폴트 템플릿 인수의 타입을 int형으로 명시
    template <typename T = int, int i>
    class X
    { ... }
  3. 클래스 템플릿을 기초 클래스로 하여 상속할 수 있다.
    // 클래스 템플릿 X를 상속받음
    template <typename T>
    class Y : public X <T>
    { ... }

특수화(specialization)

암시적 특수화(implicit specialization)

클래스 템플릿이 각각의 타입에 대해 처음으로 호출될 때, C++ 컴파일러는 해당 타입의 인스턴스(instance)를 생성한다.

생성된 인스턴스는 해당 타입에 대해 암시적으로 해당 타입에 특수화된 템플릿 함수이다.
따라서 해당 인스턴스는 함수 템플릿에 해당 타입이 사용될 때마다 호출된다.

명시적 특수화(explicit specialization)

클래스 템플릿은 특정 타입이나 값의 템플릿 인수에 대해 특수화를 할 수 있다.
특수화를 명시하면, 해당 타입에 대한 특변한 동작을 정의할 수 있다.

컴파일러는 전달된 인수에 정확히 대응하는 특수화된 정의를 발견하면, 더는 다른 템플릿을 찾지 않고 해당 정의를 사용한다.

명시적 특수화 예제

double형에 대한 명시적 특수화를 추가시킨 예제이다.

#include <iostream>

tmplate <typename T>
class Data
{
private:
	T data_;
public:
	Data(T dt);
    T get_data();
}

// double형에 대한 명시적 특수화
template <>
class Data<double>
{
private:
	double data_;
public:
	Data(double dt) { data_ = dt; }
    double get_data();
    {
    	std::cout << "double형 데이터를 출력합니다." << std::endl;
        return data_;
    }
}


int main()
{
	Data<string> str_data("클래스 템플릿");
    Data<double> double_data(3.14);
    
    std::cout << "str_data : " << str_data.get_data() << std::endl;
    std::cout << "double_data : " << double_data.get_data() << std::endl;
    return 0;
}

template <typename T>
Data<T>::Data(T dt)
{
	data_ = dt;
}

template <typename T>
T Data<T>::get_data()
{
	return data_;
}

Output

str_data : 클래스 템플릿
double형 데이터를 출력합니다.
double_data : 3.14

std::basic_string, std::string

클래스 템플릿의 대표적인 예 중에 하나는 std::basic_string이다.

std::basic_string은 문자 유형에서 문자열을 만들기 위한 클래스 탬플릿이다.

template <class charT, class traits = char_traits<charT>, class Alloc = allocator<charT>> class basic_string;

이러한 std::basic_string에 대해 char 형에 대한 명시적 특수화를 한 클래스의 typedef 한 클래스가 std::string이다.

typedef basic_string<char> string;

부분 특수화(partial specialization)

만약 템플릿 인수가 2개 이상이고, 그 중 일부에 대해서만 특수화를 해야할 때는 부분 특수화(partial specialization)을 할 수 있다.

부분 특수화 방법은 먼저 template 키워드 다음에 나오는 꺾쇠괄호(<>)에 특수화하지 않는 타입의 템틀릿 인수를 명시한다.
그리고 다음에 다오는 꺾쇠괄호(<>)에 특수화하는 타입을 명시하면 된다.

부분 특수화 예제

template <typename T1, typename T2>
class X
{ ... }

템플릿 인수가 2개인 위의 템플릿 클래스 X를 double형에 대해 부분 특수화를 하면 다음과 같다.

template <typename T1>
class X<T1, double>
{ ... }

모든 템플릿 인수를 특수화하게 되면, 앞서 살펴본 완전한 명시적 특수화가 된다.

template <>
class X<double, double>
{ ... }

중첩 클래스 템플릿(nested class template)

C++에서 클래스나 클래스 템플릿 내에 또 다른 템플릿을 중첩하여 정의할 수 있으며, 이러한 템플릿을 멤버 템플릿(member template)이라고 한다.

멤버 템플릿 중 클래스 템플릿을 중첩 클래스 템플릿(nested calss template)이라고 한다.
이러한 중첩 클래스 템플릿은 바깥쪽 클래스 범위 내에서 클래스 템플릿으로 선언되며, 정의는 바깥쪽 클래스의 범위 내애서 뿐만아니라 범위 밖에서도 가능하다.

중첩 클래스 템플릿 문법

중첩 클래스 템플릿은 다음과 같은 문법으로 정의할 수 있다.

template <typename T>
class X
{
	template <typename U>
	class Y
    {
    	// ...
    }
    
    // ...
}

int main()
{
	// ...
}

template<typename T>
template<typename U>
X<T>::Y<U>::멤버함수이름()
{
	// 멤버 함수 구현
}

이처럼 바깥쪽 클래스인 X의 외부에 중첩 클래스 템플릿 Y를 정의하면, 클래스 템플릿의 템플릿 인수와 멤버 템플릿의 템플릿 인수가 둘 다 앞에 명시되어야 한다.


템플릿을 위한 새로운 이름

C++11부터는 typedef 키워드를 이용해 템플릿 특수화를 위한 새로운 이름을 선언할 수 있다.

typedef X<double, 3.14> DoubleX;

// double_x는 X<double, 3.14> 타입이다.
DoubleX double_x;

참고 자료

0개의 댓글