본문 바로가기
Programing_Language/C++

템플릿(Template) (2/2) (템플릿 특수화)

by neohtux 2020. 12. 14.
728x90

템플릿 매개변수

  • 일반적으로 클래스 템플릿을 작성할 때는 template <typename T> 처럼
    꺽쇠괄호 안에 매개변수를 나열한다.
  • 이 매개 변수 T뿐만 아니라, 함수의 매개변수처럼 원하는 수 만큼 지정할 수 있다.
    이때, 매개변수를 타입 대신 디폴트 값으로 정해도 된다.
template<typename T, int size> //템플릿의 타입을 2개로 m_array의 size를 받는다.
 class ClassArr
{
private:
    T m_array[size];
public:
    T* getArray() { return m_array; }

    T& operator[](int index)
    {
        return m_array[index];
    }

    void print()
    {
        for (int count = 0; count < size; ++count)
            cout << (*this)[count] << ' ';

        cout << '\n';
    }
};

int main(void)
{
    const int _size = 10;
    ClassA<int, _size> sarr; //_size ClassA를 인스턴스화 할때 두번째 타입의 정수크기 만큼 배열을 할당한다.
    for (int i = 0; i < 10; ++i)
    {
        sarr[i] = i + 1; //ClassA의 객체에 1~10까지 대입
    }
  sarr.print(); //1~10출력

  return 0;
}



클래스 템플릿의 특수화

  • 특정한 경우에 대해서만 템플릿을 다르게 구현하는 것을 템플릿 특수화라고 한다.
    • ex) 형을 제외한 나머지 일반 타입<int,double,float,short..>들은 동일하게 처리하고 char형만 조금 다르게 처리하고 싶다.
 template<typename T>
class ClassA
{
public:
    void print()
    {
        cout << "일반화 일반화" << '\n';
    }
    void test() {};
};

template<>
class ClassA<char> //Specialization
{
public:

    void print()
    {
        cout << "char 특수화" << '\n';
    }

};
int main(void)
{
    ClassA<int> int_a;
    ClassA<double> double_a;
    ClassA<float> float_a;

    int_a.print();    // "일반화 일반화" 출력
    double_a.print();    // "일반화 일반화" 출력
    float_a.print();    // "일반화 일반화" 출력

    ClassA<char> char_a;

    char_a.print(); //      "char 특수화 출력
  char_a.test() // Error 컴파일 에러 , 상속과 다른 개념이다.
               // (서로 다른 클래스를 만들어서 동일한 이름으로 타입을 인슨터스화 시킬 수 있다고 생각해야함.)

    return 0;
}



c++17에서 생성자로 클래스 템플릿 특수화 하기.

  • 이 기능은 c++17 컴파일러에서 가능하다.
template<typename T>
class ClassA
{
public:
    ClassA(const T& input){} // 생성자로 구분
    void print()
    {
        cout << "일반화 일반화" << '\n';
    }
    void test() {};
};

template<>
class ClassA<char> //Specialization
{
public:

    ClassA(const char& input) {}
    void print()
    {
        cout << "char 특수화" << '\n';
    }

};
int main(void)
{
    ClassA int_a(10);
    ClassA double_a(3.14);
    ClassA float_a(2.5f);

    int_a.print();    // "일반화 일반화" 출력
    double_a.print();    // "일반화 일반화" 출력
    float_a.print();    // "일반화 일반화" 출력

    ClassA char_a('a');

    char_a.print(); //        "char 특수화 출력

    return 0;
}



상속과 특수화 비교

  • 템플릿 상속과 특수화의 차이점은 헷갈리기 쉽다.

구분 상속 특수화
코드 재사용 O : 파생 클래스는 베이스 클래스에 있는 데이터 멤버와 메서드를 모두 가진다. X: 특수화를 할 때는 필요한 코드를 모두 다시 작성해야 한다.
이름 재사용 X : 파생 클래스의 이름은 반드시 베이스 클래스와 다르게 지어야한다. O : 특수화 템플릿 클래스의 이름은 반드시 원본과 같아야 한다.
다형성 지원 O : 파생 클래스의 객체를 베이스 클래스의 객체로 표현할 수 있다. X : 템플릿을 인스턴스화한 결과마다 타입이 다르다.
  • 구현을 확장하거나 다형성을 지원 -> 상속
  • 특정한 타입에 대한 템플릿 구현을 커스터마이징 -> 특수화



템플릿을 메서드(함수)에 특수화 하기

template<typename T>
class ClassA
{
private:
    T m_value;

public:
    ClassA(const T& value) : m_value(value) {}


    template<typename Q> //func를 호출하기위해 새로운 타입을 지정하고 호출해야함.
    void func()
    {
        cout << "타입을 지정하고 호출한 함수" << '\n';
    }

    void doSomething() //타입 지정안하고 객체가 그냥 호출해도 되는 함수.
    {
        cout << "타입 없이 호출" << '\n';
    }


};


int main(void)
{
    ClassA<int> a(10);

    //a.func(); //Error : func()를 호출하려면 a.func<int>() 이런식으로 타입을 정하고 호출 해야함!!

    a.func<int>(); // "타입을 지정하고 호출한 함수" 출력

    a.doSomething();// "타입 없이 호출" 출력

    return 0;
}
300x250

댓글