본문 바로가기
42/42cursus

[ft_containers] 2. 템플릿 메타 프로그래밍과 is_integral, enable_if

by jaemjung 2022. 7. 12.

메타프로그래밍이란?

컴파일 타임에 생성되는 코드로 프로그래밍을 하는 것!

c++에서는 템플릿을 이용하여 메타프로그래밍을 하기 때문에 템플릿 메타 프로그래밍(Template Meta Programming)이라고 부름!

 

이런 템플릿 메타프로그래밍은 템플릿 인자로 타입과 더불어 값을 받아올 수 있는 점을 이용해 구현하게 된다.

#include <iostream>

template <int N>
struct Int {
  static const int num = N; //static 변수이기 때문에 struct가 인스턴스화 되지 않아도 값을 가질 수 있음
};

template <typename T, typename U>
struct add {
  typedef Int<T::num + U::num> result;
};

int main() {
  typedef Int<1> one;
  typedef Int<2> two;

  typedef add<one, two>::result three;

  std::cout << "Addtion result : " << three::num << std::endl;
}

// 실행결과 : 3

위의 코드를 실행하면 결과 값으로는 3이 출력이 되고, 이 값이 3으로 결정이 되는 시점은 런타임이 아닌 컴파일 타임임.

실제로 실행 이전에 이미 값이 3으로 고정되어 있음.

이런식으로 템플릿을 이용하면 객체를 직접 생성하지 않더라도 타입에 특정한 값을 부여할 수 있고,

이를 컴파일 타임에 연산할 수도 있게 되는데 이것이 바로 템플릿 메타 프로그래밍이다!

 

이러한 메타프로그래밍의 특성을 이용하여 컴파일 타임에 템플릿 인자로 들어온 타입이 정확히 어떤 타입인지 확인해 줄 수 있게된다.

 

먼저 다음과 같이 is_integral의 기반 구조체를 정의한다.

template <bool is_integral, typename T>
struct is_integral_base {
	typedef T type;
	const static bool value = is_integral;
};

그리고 기본적으로 아무런 타입을 받지 않았을 때의 is_integral의 템플릿 클래스를 다음과 같이 정의한 후,

template <typename>
struct is_integral : public is_integral_base<false, void> {};

각 템플릿의 타입별로 다음과 같이 템플릿 클래스를 오버로딩 해준다.

template <>
struct is_integral<bool> : public is_integral_base<true, bool> {};

template <>
struct is_integral<char> : public is_integral_base<true, char> {}; 

template <>
struct is_integral<unsigned char> : public is_integral_base<true, unsigned char> {};

template <>
struct is_integral<int> : public is_integral_base<true, int> {};

template <>
struct is_integral<unsigned int> : public is_integral_base<true, unsigned int> {};
    
// ... 그 외의 모든 literal type에 대해 오버로딩

이렇게 정의를 해두면,

is_integral<sometype>과 같이 사용하면 컴파일 타임에 is_integral_base 내부에 있는 type과 value가 결정된다.

왜냐? 컴파일러가 선언된 is_integral<sometype>을 실행하기 위해 적절한 템플릿 오버로딩을 찾아서 is_integral_base의 멤버변수 value와 type을 결정하기 때문!

만약에 sometype이 리터럴 타입이 아닌 사용자가 정의한 클래스 등이라면 리터럴 타입에 따라 특수화 되어있는 템플릿이 아닌 기본 템플릿 클래스를 사용하여 value 값이 false로 초기화된다.

따라서 컴파일 타임에 sometype이 리터럴인지 아닌지 판별할 수 있게 되는 것이다.

 

동일한 원리를 이용하여 컴파일 타임에 메타프로그래밍의 결과가 false인지 true인지 판별해주는 enable_if 역시 구현해 줄 수 있다.

template <bool cond, typename T = void>
struct enable_if {};

template <typename T>
struct enable_if<true, T> {
	typedef T type;
};

이 경우 enable_if에 전달되는 인자값이 true인 경우만 템플릿 특수화를 이용해 type을 멤버변수로 가지는 타입을 생성한다. 따라서 해당 멤버변수에 접근이 가능하다면 컴파일 타임에 특정한 메타프로그래밍 결과 값이 true라고 판단할 수 있다. 

 

댓글