C++ Chapter 13.5 : 클래스 템플릿 특수화

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!


chapter 13. 템플릿 : 클래스 템플릿 특수화

특수화 : 템플릿에서 특정 타입에 대해서 다른 실행 처리를 하고 싶을 때 그 타입에 대해서만 특수화.

🔔 클래스 템플릿 특수화

template<>
class A<char>
{
};

T를 구체화 하니까 T를 제외한 typename<>만 써준 후 클래스 이름 옆에 <특수화 할 타입> 를 명시해준다.

  • 해당 클래스로 객체를 생성할 때 char 타입으로 구체화할 때만 위 클래스로 구체화된다.
#include <iostream>

using namespace std;

template<typename T>
class A 
{
public:
    A(const T& input)
    {}

    void doSomething()
    {
        cout << typeid(T).name() << endl;
    }
    
    void test()
    {}
};


template<>
class A<char>
{
public:
    A(const char& input)
    {}

    void doSomething()
    {
        cout << "Char type specialization" << endl;
    }
};

int main()
{
    A<int> a_int(1);
    A<double> a_double(3.14);
    A<char> a_char('a');
    
    a_int.doSomething();
    a_double.doSomething();
    a_char.doSomething();
    
    a_char.test(); // ❌에러❌

    return 0;
}
  • char타입으로 구체화 될 땐 다른 타입들로 구체화될 때와 다르게 test() 멤버 함수가 없는 것이나 마찬가지다.
    • char타입으로 구체화 될 땐 첫번째 클래스 템플릿으로 구체화되는게 아니라 아래 두번째 클래스 템플릿으로 구체화되기 때문이다.
      template<>
      class A<char>
      {
      public:
          A(const char& input)
          {}
      
          void doSomething()
          {
              cout << "Char type specialization" << endl;
          }
      };
      
      A<char> a_char('a');
      a_char.test(); // ❌에러❌
      
  • char타입으로 구체화 될 때만 위 클래스로 객체를 찍어내기 때문에 생성자도 따로 정의해주어야 한다.
    • 그렇지 않으면 그냥 빈 내용의 디폴트 생성자를 컴파일러가 만들어 호출함!
    • 원래 생성자 정의가 없었다면 상관 없지만 원래 클래스 템플릿에 생성자가 있었다면 정의해주자.
  • C++ 17 부터는 생성자 매개변수로 들어가는 인수로 T 타입을 알 수 있는 경우 <구체화타입>을 생략할 수 있다.
      A a_int(1);   // 👉 int 로 구체화
      A a_double(3.14);  // 👉 double 로 구체화
      A a_char('a');   // 👉 char 로 구체화
    


🔔 응용

크기 8 의 배열을 멤버로 가지는 클래스를 만든다고 할 때, 어차피 bool은 1bit로 표현할 수 있으므로 특별히 bool 타입일때만 1 byte = 8 bit 크기인 char 타입의 데이터 1 개를 마치 8 개의 bit 배열인 것처럼 구현하자.

#include <iostream>

using namespace std;

template<typename T>
class Storage8
{
private:
    T m_array[8];
public:
    void set(int index, const T &value)
    {
        m_array[index] = value;
    }
    
    const T& get(int index)
    {
        return m_array[index];
    }
};


template<>
class Storage8<bool>
{
private:
    unsigned char m_data;
public:
    Storage8() 
        : m_data(0)
    {}

    void set(int index, bool value)
    {
        unsigned char mask = 1 << index;
        
        if (value)
            m_data |= mask;
        else
            m_data &= ~mask;
    }
    
    bool get(int index)
    {
        unsigned char mask = 1 << index;
        return (m_data & mask) != 0;
    }
};


int main()
{
    Storage8<int> intStorage;
    
    for (int count = 0; count < 8; ++count)
        intStorage.set(count, count);
    
    for (int count = 0; count < 8; ++count)
        cout << intStorage.get(count) << " ";
    
    cout << " Sizeof Storage8<int> " << sizeof(Storage8<int>) << endl;
    
    
    Storage8<bool> boolStorage;
    
    for (int count = 0; count < 8; ++count)
        boolStorage.set(count, count & 3);
    
    for (int count = 0; count < 8; ++count)
        cout << boolStorage.get(count) << " ";
    
    cout << " Sizeof Storage8<bool> " << sizeof(Storage8<bool>) << endl;
    
    return 0;
}
  • bool 타입일 때 클래스 템플릿 특수화
    template<>
      class Storage8<bool>
      {
      private:
          unsigned char m_data;
      public:
          Storage8() 
              : m_data(0)
          {}
    
          void set(int index, bool value)
          {
              unsigned char mask = 1 << index;
            
              if (value)
                  m_data |= mask;
              else
                  m_data &= ~mask;
          }
        
          bool get(int index)
          {
              unsigned char mask = 1 << index;
              return (m_data & mask) != 0;
          }
      };
    
    • bool 타입으로 구체화할 때만
      • 배열을 쓰지 않고 unsigned char 타입의 데이터 m_data 하나를 싸용한다.
        • 이 1byte = 8bit로 8가지의 ture or false를 표현할 것.
      • 특별히 생성자 있다!
        • m_data 를 0 으로 초기화함.
      • set(int index, bool value) index 번째 자리에 value 값을 set한다.

        • 예를들어 index가 5 라면
          • unsigend char m_data의 5 번째 자리에 bool value의 결과인 0 or 1 을 저장해야 하기 때문에 00000001 을 5 만큼 왼쪽 shift 연산 해주고 이를 mask에 대입.
            • mask = 1 << 5 👉 00100000
        • bool value가 True 라면
          • unsigend char m_data의 5번째 자리를 1로 만들어주어야 한다.
            • m_data |= mask 👉 m_data |= 00100000
        • bool value가 False 라면
          • unsigend char m_data의 5번째 자리를 0으로 만들어주어야 한다.
            • m_data &= ~mask 👉 m_data |= 00100000
      • get(int index) index 번째 자리의 value 값을 get 리턴한다.

        • 예를들어 index가 4 라면
          • unsigend char m_data의 4 번째 자리의 bit 값이 1 이면 True 리턴, 0 이면 False 리턴 식으로 구현해야 한다.
          • 00000001 을 4 만큼 왼쪽 shift 연산 해준다.
          • mask = 1 << 4 👉 00010000
          • mask는 4번째 자리만 1인 비트가 된다.
          • m_data & mask 결과가 mask = 00010000 이면, 즉 0이 아니면 True 리턴
          • m_data & mask 결과가 00000000 이면, 즉 0이면 False 리턴
  • 출력
    0 1 2 3 4 5 6 7 Sizeof Storage8<int> 32
    0 1 1 1 0 1 1 1 Sizeof Storage8<bool> 1
    
    • int 로 구체화 되었을 땐 8 크기의 int 배열 멤버로 8 * 4byte = 32 byte 로 크키가 32로 나온 것을 볼 수 있다.
    • bool로 구체화 되었을 땐 char 멤버 하나로 1 byte 크기로 크기가 1 이 나온 것을 알 수 있다.
      • bool 타입이 구체화가 되지 않고 그냥 bool도 8 크기의 bool 배열 멤버를 사용했다면 8 이 나왔을 것이다.
        • bool 원래는 1 byte니까. 1bit로 표현할 수 있긴 하지만!


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Cpp 카테고리 내 다른 글 보러가기

댓글 남기기