728x90

이전 포스팅에서 간단히 템플릿에 대해서 설명하였습니다.

 

[C++] Template(1)

기본적으로 템플릿은 편의성과 확장성을 모두 만족할 수 있는 기법입니다. 예를 들어 매개변수로 전달받은 두 수를 더하는 함수를 만든다고 가정했을 때 매개변수는 정수형이 올 수도 실수형이

1d1cblog.tistory.com

우리가 template과 함께 typename 이라는 키워드를 사용하였습니다. 이 typename이라는 키워드는 템플릿 매개변수를 선언할 때 사용합니다. template <typename T> 처럼 사용하는 사람이 있고 template <class T> 로 사용하는 사람이 있는데 똑같이 동작합니다. typename이라는 키워드 자체를 타입이라고 명시를 시켜줄 때 사용할 수도 있습니다.

 

예를 들어 아래와 같이 코드를 작성했을 때 일반적인 상황에서는 전달받은 컨테이너가 가지고 있는 const_iterator 타입을 포인터 변수로 사용하는 코드가 됩니다. 하지만 만약에 전달받은 타입에 const_iterator라는 정적 변수가 있을 때에는 또 다른 의미를 가지게 됩니다. 그리고 x라는 전역 변수가 어딘가에 있다면 이 구문은 포인터 변수를 선언하는 코드가 아닌 단순 곱하기처럼 동작할 가능성을 지니게 됩니다.

template <typename T>
void print(const T& container)
{
	T::const_iterator * x;
}

그렇기에 이러한 문제를 방지하기 위해 이것은 타입이다 라고 명시할 수 있게 typename 키워드를 앞에 붙여서 사용할 수 있습니다.

template <typename T>
void print(const T& container)
{
	typename T::const_iterator * x;
}

템플릿으로 만들어진 클래스를 상속 받아 클래스를 하나 만들어 보겠습니다. Printer라는 클래스는 Test 변수를 하나 생성해서 printTest 함수를 호출합니다. 근데 이 Printer<T>를 상속받아 LogPrinter라는 클래스를 하나 더 만들고 이 안에서 print 함수를 호출하도록 하였습니다. 하지만 이대로 컴파일 시 에러가 나게 됩니다.

#include <iostream>

class Test {
public:
  void printTest() { std::cout << "Test"; }
};

template <typename T>
class Printer {
public:
  void print() {
    T t;
    t.printTest();
  }
};

template <typename T>
class LogPrinter : public Printer<T> {
public:  
  void lprint() {
    print();
  }
};

int main()
{
  LogPrinter<Test> lp;
  lp.lprint();
}

이 print라는 함수를 컴파일 단계에서는 기본 클래스를 명확히 할 수 없기 때문에 컴파일 할 수가 없습니다. 기본 클래스가 전달받은 타입에 따라 print라는 함수가 없을 수도 있기 때문입니다. 이러한 에러를 해결하기 위해서는 아래 방법을 사용할 수 있습니다.

// this 사용
template <typename T>
class LogPrinter : public Printer<T> {
public:  
  void lprint() {
    this->print();
  }
};

// using을 통해 기본 클래스에 함수가 있음을 알려줌
template <typename T>
class LogPrinter : public Printer<T> {
public:
  using Printer<T>::print;
  void lprint() {
    print();
  }
};

// 명시적으로 기본 클래스의 함수 호출
template <typename T>
class LogPrinter : public Printer<T> {
public:  
  void lprint() {
    Printer<T>::print();
  }
};

이렇게 템플릿을 사용하면 코드의 수를 줄일 수 있을 뿐 아니라 유지보수 및 기타 부분에 대해서 장점을 지니게 됩니다. 하지만 이런 템플릿도 잘못된 방법으로 사용할 경우 쓸데없이 코드가 비대해질 수 있습니다. 아래 코드로 예를 들어보겠습니다.

 

아래와 같이 타입과 크기를 전달받는 클래스가 있을 때 아래와 같이 사용한다면 Array<int, 10>::invert와 Array<int, 20>::invert에 대한 인스턴스화 되게 됩니다. 

template <typename T, int size>
class Array {
public:
    void invert();
};

int main()
{
    Array<int, 10> AInt10;
    Array<int, 20> AInt20;
    
    AInt10.invert();
    AInt20.invert();
}

이러한 중복을 막기 위해서는 굳이 사이즈 값은 템플릿으로 받지 않고 멤버 함수의 매개변수로 받아 처리할 수도 있습니다.

template <typename T>
class Array {
public:
    void invert(int nSize);
};

int main()
{
    Array<int> AInt10;
    Array<int> AInt20;
    
    AInt10.invert(10);
    AInt20.invert(20);
}
728x90

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

[C++] boost windows 비동기 처리하기  (2) 2023.10.23
[C++] Template(1)  (2) 2023.09.19
[C++] std::forward_list, std::list  (0) 2023.07.18
[C++] std::array, std::vector  (0) 2023.07.12
[C++] 람다 함수  (0) 2023.04.07

+ Recent posts