C++/Templates 2021. 3. 21. 23:59

클래스 템플릿 (Class Templates)

  • 클래스에도 템플릿을 적용할 수 있다.

예제

  • 템플릿을 사용하지 않은 예제

    main.cpp

    #include "MyArray.h"
    
    int            main()
    {
      MyArray my_array(10);
    
      for (int i = 0; i < my_array.getLength(); ++i)
        my_array[i] = i * 10;
    
      my_array.print();
    }
    
    /* stdout
    0 10 20 30 40 50 60 70 80 90
    */

    MyArray.h

    #pragma once
    #include <iostream>
    #include <cassert>
    
    class MyArray
    {
      int  length_;
      int* data_;
    
    public:
      MyArray()
      {
        length_ = 0;
        data_ = nullptr;
      }
    
      MyArray(const int& length_in)
      {
        data_ = new int[length_in];
        length_ = length_in;
      }
    
      ~MyArray()
      {
        reset();
      }
    
      void reset()
      {
        delete[] data_;
        data_ = nullptr;
        length_ = 0;
      }
    
      int& operator[](int index)
      {
        assert(index >= 0 && index < length_);
        return data_[index];
      }
    
      int getLength()
      {
        return length_;
      }
    
      void print()
      {
        for (int i = 0; i < length_; ++i)
          std::cout << data_[i] << ' ';
        std::cout << std::endl;
      }
    };

  • 템플릿을 적용한 예제

    main.cpp

    #include "MyArray.h"
    
    int            main()
    {
      MyArray<int> my_array(10);
    
      for (int i = 0; i < my_array.getLength(); ++i)
        my_array[i] = i * 10;
    
      my_array.print();
    }
    
    /* stdout
    0 10 20 30 40 50 60 70 80 90
    */

    MyArray.h

    #pragma once
    #include <iostream>
    #include <cassert>
    
    template<typename T>
    class MyArray
    {
      int        length_;
      T*        data_;
    
    public:
      MyArray()
      {
        length_ = 0;
        data_ = nullptr;
      }
    
      MyArray(const int& length_in)
      {
        data_ = new T[length_in];
        length_ = length_in;
      }
    
      ~MyArray()
      {
        reset();
      }
    
      void reset()
      {
        delete[] data_;
        data_ = nullptr;
        length_ = 0;
      }
    
      T& operator[](int index)
      {
        assert(index >= 0 && index < length_);
        return data_[index];
      }
    
      int getLength()
      {
        return length_;
      }
    
      void print()
      {
        for (int i = 0; i < length_; ++i)
          std::cout << data_[i] << ' ';
        std::cout << std::endl;
      }
    };
    • main.cpp 에서 다른 자료형을 넣어도 작동한다.

      #include "MyArray.h"
      
      int            main()
      {
        MyArray<double> my_array(10);
      
        for (int i = 0; i < my_array.getLength(); ++i)
          my_array[i] = i * 0.5;
      
        my_array.print();
      }
      
      /* stdout
      0 0.5 1 1.5 2 2.5 3 3.5 4 4.5
      */
      #include "MyArray.h"
      
      int            main()
      {
        MyArray<char> my_array(10);
      
        for (int i = 0; i < my_array.getLength(); ++i)
          my_array[i] = i + 65;
      
        my_array.print();
      }
      
      /* stdout
      A B C D E F G H I J
      */

헤더 파일과 .cpp 파일의 분리

  • 헤더에 함수를 정의했는데, 이를 .cpp 파일로 분리해야 한다.

  • 템플릿을 사용하면 이 과정이 살짝 번거롭다.

  • 일단 헤더 파일에서만 print 함수를 분리해보면 다음과 같다.

    MyArray.h

    #pragma once
    #include <iostream>
    #include <cassert>
    
    template<typename T>
    class MyArray
    {
      int        length_;
      T*        data_;
    
    public:
      MyArray()
      {
        length_ = 0;
        data_ = nullptr;
      }
    
      MyArray(const int& length_in)
      {
        data_ = new T[length_in];
        length_ = length_in;
      }
    
      ~MyArray()
      {
        reset();
      }
    
      void reset()
      {
        delete[] data_;
        data_ = nullptr;
        length_ = 0;
      }
    
      T& operator[](int index)
      {
        assert(index >= 0 && index < length_);
        return data_[index];
      }
    
      int getLength()
      {
        return length_;
      }
    
      void print();
    };
    
    template<typename T>
    void MyArray<T>::print()
    {
      for (int i = 0; i < length_; ++i)
        std::cout << data_[i] << ' ';
      std::cout << std::endl;
    }
    • 이대로 컴파일하면 아무 문제가 없다.
  • 다음은 MyArray.cpp 파일로 옮기는 과정이다.

    • 헤더 파일에서 분리한 함수 정의 부분을 잘라내서, .cpp 파일에 그대로 붙여넣기한다.

      MyArray.cpp

      #include "MyArray.h"
      
      template<typename T>
      void MyArray<T>::print()
      {
        for (int i = 0; i < length_; ++i)
          std::cout << data_[i] << ' ';
        std::cout << std::endl;
      }
      error LNK2019: unresolved external symbol "public: void __cdecl MyArray<char>::print(void)" (?print@?$MyArray@D@@QEAAXXZ) referenced in function main
      • main.cpp에서 MyArray.cpp에 정의된 함수의 body 내용을 알 수 없기 때문에 발생하는 문제라고 한다.

        • 템플릿의 작동 원리를 알아야 완벽하게 파악할 수 있을 것 같다.
      • main.cpp에서 MyArray.cppinclude 하면 해결되긴 하는데, 이렇게 하는 것은 좋지 않다고 한다.

      • 대신 explicit instantiation을 해야하는데, 명시적으로 템플릿을 적용하는 것이다.


Explicit Instantiation

  • 클래스나 함수에 적용할 템플릿을 명시해주는 것이다.

    MyArray.cpp

    #include "MyArray.h"
    
    template<typename T>
    void MyArray<T>::print()
    {
      for (int i = 0; i < length_; ++i)
        std::cout << data_[i] << ' ';
      std::cout << std::endl;
    }
    
    template class MyArray<char>;
    template void MyArray<double>::print();