티스토리 뷰

C++/C to C++

[C to C++] 템플릿

nodeal 2019. 2. 1. 12:37

추가되는 기능

    • 사용되는 타입이 다른 타입으로도 같은 수행이 보장되어 일반화할 수 있을 때 template을 사용한다.
      int add(int a, int b) {
          return a + b;
      }
      
      float add(float a, float b) {
          return a + b;
      }
      
      int sum_int = add(10, 20); // = 30
      float sum_float = add(10.5f, 20.5f); // = 31
      
      template<typename T>
      T add(T a, T b) {
          return a + b;
      }
      
      int sum_int = add(10, 20);
      float sum_float = add(10.5f, 20.5f);

      template을 사용할 경우 코드의 재사용성이 높아지며 같은 코드를 반복적으로 작성할 필요가 없어진다. 이때 필요한 타입에 대한 추론이 컴파일러에 의해 실행되며 각 사용에 맞는 타입의 함수로 생성된다.

    • Class 또는 구조체에 대해서도 template을 적용할 수 있다. 또한 class와 구조체도 template의 인자로 쓰일 수 있다.
      #include <iostream>
      
      template<typename T>
      class holder {
          private:
          T data;
          
          public:
          T get() const {
              return data;
          }
          
          void set(T data) {
              this->data = data;
          }
      };
      
      int main() {
          holder<int> holder_i;
          holder_i.set(10);
          std::cout << holder_i.get() << std::endl;
          
          holder<holder<int>> holder_holder_i;
          holder_holder_i.set(holder_i);
          std::cout << holder_holder_i.get().get() << std::endl;
          
          return 0;
      }
      
    • Template에도 기본값을 정할 수 있다. 기본값이 주어졌고 호출에서 명시적 선언이 없다면 기본값을 사용하게 된다.

      #include <iostream>
      
      template<typename T = int>
      class holder {
          private:
          T data;
          
          public:
          T get() const {
              return data;
          }
          
          void set(T data) {
              this->data = data;
          }
      };
      
      int main() {
          holder holder_i;
          holder_i.set(10);
          std::cout << holder_i.get() << std::endl;
          
          return 0;
      }
      
    • 함수 템플릿에서 타입이 모호할 경우 명시적으로 타입을 지정해줄 수 있다.

      #include <iostream> #include <string> template<typename T> T add(T a, T b) { return a + b; } int main() { std::cout << add(10, 20) << std::endl; std::cout << add<std::string>("Hello", "World") << std::endl; return 0; }

      30
      HelloWorld
      

      C++에서는 string literal을 const char*로 처리하기때문에 덧셈 이항 연산자(operator+)를 이용하여 char*간 연산이 불가능하다. 따라서 명시적으로 std::string임을 지정해 두 string literal을 이은 결과를 반환할 수 있다.

    • Template은 타입뿐만 아니라 compile-time constant에 대해서도 정의할 수 있다. constexpr 기호를 이용하여 외부 변수로 상수를 정의할 때, 해당 코드를 수정할 수 없다면 외부에서 고정된 값을 사용해야하므로 이 방법을 사용하는 것이 좋다. 또한, 기본값을 정할 수 있으므로 상수로의 역할도 똑같이 실행할 수 있으므로 되도록 #define과 constexpr을 이용하여 객체의 가변 상수를 지정하는 방법을 피하자.
      #include <iostream>
      
      template<typename T = int, T INITIAL = 10>
      class holder {
          private:
          T data = INITIAL;
          
          public:
          T get() const {
              return data;
          }
          
          void set(T data) {
              this->data = data;
          }
      };
      
      int main() {
          holder<int, 100> holder_i;
          std::cout << holder_i.get() << std::endl;
          
          return 0;
      }

목표

    • 반복되어 사용되고 대체될 수 있는 타입에 대해 템플릿을 적용한다.
    • 외부에서 지정할 수 있는 기본값에 대해 템플릿을 적용한다.

디자인

    • class vector에 다음 템플릿 인자를 추가한다.
      • typename T
      • int INITIAL_SIZE = 10

    • 다음 함수의 선언이 템플릿 인자로 바뀐다.
      • void add(int) => void add(T)
      • void add(int, int) => void add(int, T)
      • int get(int) => T get(int)
      • int set(int, int) => T set(int, T)
      • int remove(int) => T remove(int)
      • vector& operator+=(int) => vector& operator+=(T)

구현

#include <iostream>
#include <algorithm>

template<typename T, int INITIAL_SIZE = 10>
class vector {
    private:
    T* data;
    int capacity;
    int length;
    
    bool ensure_capacity(int to_add) const {
        return length + to_add < capacity;
    }
    
    void increase_capacity() {
        auto tmp = data;
        data = new T[capacity * 2];
        std::copy(tmp, tmp + length, data);
        delete[] tmp;
        capacity *= 2;
    }
    
    void shiftLeft(int offset, int width) {
        int tail_length = length - offset - width;
        T* tail = new T[tail_length];
        std::copy(data + offset + width, data + length, tail);
        std::copy(tail, tail + tail_length, data + offset);
        delete[] tail;
    }
    
    void shiftRight(int offset, int width) {
        int tail_length = length - offset;
        T* tail = new T[tail_length];
        std::copy(data + offset, data + length, tail);
        std::copy(tail, tail + tail_length, data + offset + width);
        delete[] tail;
    }
    
    public:
    vector() {
        data = new T[INITIAL_SIZE];
        capacity = INITIAL_SIZE;
        length = 0;
    }
    
    vector(const vector& v) {
        data = new T[v.capacity];
        capacity = v.capacity;
        length = v.length;
        std::copy(v.data, v.data + v.length, data);
    }
    
    ~vector() {
        delete[] data;
    }
    
    void add(T element) {
        if (!ensure_capacity(1))
            increase_capacity();
        
        *(data + length++) = element;
    }
    
    void add(int index, T element) {
        if (!ensure_capacity(1))
            increase_capacity();
        
        shiftRight(index, 1);
        *(data + index) = element;
        length++;
    }
    
    T get(int index) const {
        return *(data + index);
    }
    
    T set(int index, T element) {
        auto tmp = *(data + index);
        *(data + index) = element;
        return tmp;
    }
    
    T remove(int index) {
        auto tmp = *(data + index);
        shiftLeft(index, 1);
        length--;
        
        return tmp;
    }
    
    void print() const {
        std::cout << '{';
        for (int i = 0; i < length; i++)
            std::cout << *(data + i) << ((i < length - 1) ? ", " : "");
        std::cout << '}' << std::endl;
    }
    
    vector& operator=(const vector& v) {
        data = new T[v.capacity];
        capacity = v.capacity;
        length = v.length;
        std::copy(v.data, v.data + capacity, data);
        
        return *this;
    }
    
    vector& operator+=(T element) {
        add(element);
        
        return *this;
    }
    
    T& operator[](int index) {
        return *(data + index);
    }
    
    T operator[](int index) const {
        return *(data + index);
    }
};


'C++ > C to C++' 카테고리의 다른 글

[C to C++] 상속 - 생존주기, 가상함수  (0) 2019.02.02
[C to C++] 상속  (0) 2019.02.01
[C to C++] 템플릿  (0) 2019.02.01
[C to C++] friend, 연산자 오버로딩  (0) 2019.01.31
[C to C++] 함수 오버로딩  (0) 2019.01.31
[C to C++] 생성자, 소멸자, 복사 생성자  (0) 2019.01.29
댓글
댓글쓰기 폼