Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C++] 템플릿 인자 본문

Programming/C++

[Programming/C++] 템플릿 인자

scii 2021. 2. 7. 17:53

템플릿을 정의할 때 결정되지 않은 자료형을 의미하는 용도로 사용되는 T 또는 T1, T2와 같은 문자를 가리켜 '템플릿 매개변수' 라 한다.
그리고 템플릿 매개변수에 전달되는 자료형 정보를 가리켜 '템플릿 인자' 라 한다.

템플릿 매개변수에는 변수의 선언이 올 수 있다.

template <typename T, int len>
class SimpleArray {
  private:
    T arr[len];
    
  public:
    T& operator[] (int idx) {
      return arr[idx];
    }
};

이렇듯 템플릿 매개변수에도 변수가 올 수 있다. 그리고 이를 기반으로 다음의 형태로 객체생성이 가능하다.

SimpleArray<int, 5> i5arr;
SimpleArray<double, 7> d7arr;

위의 두 문장에서 템플릿 매개변수 len에 전달된 인자 5와 7은 해당 템플릿 클래스에서 상수처럼 사용된다.

즉, len은 각각 5와 7로 치환되어, 컴파일러에 의해 다음과 같이 SimpleArray<int, 5> 형 템플릿 클래스와 SimpleArray<double, 7> 형 템플릿 클래스가 각각 생성된다.

class SimpleArray<int, 5> {
  private:
    int arr[5];
    
  public:
    int& operator[] (int idx) {
      return arr[idx];
    }
};
class SimpleArray<int, 7> {
  private:
    int arr[7];
    
  public:
    int& operator[] (int idx) {
      return arr[idx];
    }
};

물론 위의 두 템플릿 클래스 SimpleArray<int, 5>와 SimpleArray<double, 7>은 서로 다른 자료형의 클래스로 구분된다.

다음의 예제를 살펴보자.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


template <typename T, int len>
class SimpleArray {
private:
    T arr[len];

public:
    T& operator[](int idx) {
        return arr[idx];
    }

    SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref) {
        for (int i = 0; i < len; i++) {
            arr[i] = ref.arr[i];
        }
        return *this;
    }
};


 int main(const int argc, const char* const argv[]) { 
     SimpleArray<int, 5> i5arr1;
     for (int i = 0; i < 5; i++) {
         i5arr1[i] = i * 10;
     }

     SimpleArray<int, 5> i5arr2;
     i5arr2 = i5arr1;
     for (int i = 0; i < 5; i++) {
         cout << i5arr2[i] << ", ";
     }
     cout << endl;

     SimpleArray<int, 7> i7arr1;
     for (int i = 0; i < 7; i++) {
         i7arr1[i] = i * 10;
     }

     SimpleArray<int, 7> i7arr2;
     i7arr2 = i7arr1;
     for (int i = 0; i < 7; i++) {
         cout << i7arr2[i] << ", ";
     }
     cout << endl;

     return 0;
}
 
/* 결과

0, 10, 20, 30, 40,
0, 10, 20, 30, 40, 50, 60,

*/

i5arr1과 i5arr2가 모두 SimpleArray<int, 5>형이기 때문에 대입연산이 가능하다.
i7arr1과 i7arr2가 모두 SimpleArray<int, 7>형이기 때문에 대입연산이 가능하다.

그리고 이 예제에서 중요한 것은 다음과 같다.

SimpleArray<int, 5>와 SimpleArray<int, 7>은 서로 다른 형(type)이다.

때문에 길이가 다른 두 배열 객체간의 대입은 허용되지 않는다. 즉, 다음의 대입연산에서는 컴파일 에러가 발생한다.

int main(void) {
  SimpleArray<int, 5> i5arr;
  SimpleArray<int, 7> i7arr;
  
  i5arr = i7arr;
  
  return 0;
}

이렇듯, 템플릿 매개변수에 값을 전달받을 수 있는 변수를 선언하면, 변수에 전달되는 상수를 통해서 서로 다른 형의 클래스가 생성되게 할 수 있다. 

따라서 위 예제의 경우, 길이가 다른 두 배열 객체간의 대입 및 복사에 대한 부분을 신경 쓰지 않아도 된다.

만약 생성자를 이용해서 배열의 길이를 결정하게 했다면, 길이가 같은 배열에 대해서만 대입을 허용하기 위해서 추가적인 코드의 삽입이 불가피하며, 이러한 추가적인 코드는 대입 및 복사의 과정에서 CPU가 수행해야 할 일을 늘리는 결과로 이어진다.


템플릿 매개변수는 디폴트 값 지정도 가능하다.

함수의 매개변수에 디폴트 값의 지정이 가능하듯이, 템플릿 매개변수에도 디폴트 값의 지정이 가능하다.

#include <iostream>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


// Default 값 지정
template <typename T = int, int len = 7>
class SimpleArray {
private:
    T arr[len];

public:
    T& operator[](int idx) {
        return arr[idx];
    }

    SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref) {
        for (int i = 0; i < len; i++) {
            arr[i] = ref.arr[i];
        }
        return *this;
    }
};


 int main(const int argc, const char* const argv[]) { 
     SimpleArray<> arr;
     for (int i = 0; i < 7; i++) {
         arr[i] = i + 1;
     }
     for (int i = 0; i < 7; i++) {
         cout << arr[i] << " ";
     }
     cout << endl;

     return 0;
}
 
/* 결과

1 2 3 4 5 6 7

*/
template <typename T = int, int len = 7>

템플릿 매개변수에 디폴트 값이 지정되어도, 템플릿 클래스의 객체생성을 의미하는 <> 기호는 반드시 추가되어야 한다. 비록 그 안을 비워둘지라도 말이다.

Comments