본문 바로가기
NC University/Advanced C++

2일차 - Template 부분 전문화(2)

by 날쑤 2015. 6. 30.

  처음에 다뤘던 템플릿 부분 전문화를 좀 더 활용해보자.

#include <iostream> 

using namespace std;  

template <typename T>  
void Print (const T& a) {  cout << T::N << endl; }  

// primary template, 임의 타입의 자료 2개를 보관하는 구조체 
template <typename T, typename U> 
struct Duo {   
	T v1;   
	U v2;   
	enum { N = 2 }; 
};

 

  이 때 T와 U는 임의의 타입을 받기때문에 T나 U자리에 아래와 같이 Duo 템플릿으로부터 파생된 타입이 오더라도 문제가 없다.

int main(int argc, char* argv[]) {   
	Duo<int, int> d2;   
	Duo<int, Duo<int, int>> d3;   
	Duo<int, Duo<int, Duo<int, int>>> d4;    
	
	Print(d2);  //< 2   
	Print(d3);  //< 2?   
	Print(d4);  //< 2?    
	return 0;
}

 

  그런데 원래 의도는 N은 객체가 저장하고 있는 값의 갯수인데, d3와 d4는 이에 부합하지 못한다. 이는 recursive한 타입인자에 대한 처리가 되어 있지 않기 때문이다. 이를 처리하기 위해서 템플릿 부분 전문화가 다시 등장한다.

// 1번째 인자가 recursive일 때 
// primary template이 타입인자가 2개라도 부분전문화에서는 개수가 다를 수 있음. 
template <typename A, typename B, typename C> 
struct Duo<Duo<A, B>, C> {   
	Duo<A,B> v1;   
	C v2;    
	enum { N = 1 + Duo<A, B>::N }; 
};  

// 2번째 인자가 'recursive Duo'일 때를 위한 부분 전문화 
template <typename A, typename B, typename C>  
struct Duo<A, Duo<B, C>> {   
	A v1;   
	Duo<B, C> v2;    
	enum { N = 1 + Duo<B, C>::N }; 
};  

// 1, 2번째 인자가 모두 recursive일 때 
template <typename A, typename B, typename C, typename D> 
struct Duo<Duo<A, B>, Duo<C, D>> {   
	Duo<A, B> v1;   
	Duo<C, D> v2;    
	enum { N = Duo<A, B>::N + Duo<C, D>::N }; 
};

  이제는 아래의 Print함수 호출들이 정상적으로 의도된 값을 출력한다.

Duo<Duo<int, char>, double> d1; 
Duo<short, Duo<Duo<int, double>, char>> d2; 
Duo<Duo<int, int>, Duo<int, int>> d3;  

Print(d1);  //< 3 
Print(d2);  //< 4 
Print(d3);  //< 4

 

  Recursive Duo는 임의 개수의 서로 다른 타입의 값들을 저장할 수 있다. 하지만 위에서 본 것 처럼 2개 이상의 값을 저장해야 하는 경우에는 반복적으로 Duo 템플릿 클래스를 이용해서 타입을 정의해야 한다. 만약 저장할 타입들을 리스트처럼 '선형적'으로 나열해서 전체 타입을 정의할 수 있다면 좋지 않을까?

  이번에는 부분 전문화를 활용해서 위의 Duo 구조체 템플릿을 선형화하는 기술을 살펴보자.

// Recursive Duo의 선형화 기술 
struct Null {};  

template <typename P1 = Null, 
	  typename P2 = Null, typename P3 = Null, typename P4 = Null, typename P5 = Null> 
class Tuple : public Duo<P1, Tuple<P2, P3, P4, P5, Null>> { 
public:   
	enum { N = 1 + Tuple<P2, P3, P4, P5, Null>::N }; 
};

// 유효한 타입이 두개인 Tuple을 위한 부분전문화 
template <typename P1, typename P2> 
class Tuple<P1, P2, Null, Null, Null> : public Duo<P1, P2> {};   

int main(int argc, char* argv[]) {   
	Tuple<int, char, double> t   
	Print(t);  //< 3   
	return 0; 
}

 

  Tuple<int, char, double>은 정확히는 Tuple<int, char, double, Null, Null>이다. 이는 Tuple의 디폴트 타입인자가 Null 타입이기 때문이다. 정의를 이용하여 t가 저장하는 값들의 타입을 나열해보면 다음과 같다.

  • int --> t.v1
  • char --> t.v2.v1
  • double --> t.v2.v2

  위의 Tuple은 최대 5개의 값만을 저장할 수 있으며, 값을 대입하거나 얻어내는 일이 꽤 번거롭다. 하지만 부분 전문화를 활용해서 복잡했던 타입 선언을 단순하게 바꿀 수 있다는 의미를 가지는 예제이다. 차후에 가변인자 템플릿이 등장하면 이를 보완하는 Tuple을 새로 작성하게 될 것이다.

IfThenElse 기술

template <bool, typename T, typename U> 
struct IfThenElse  {   
	typedef T ResultType; 
};  

// 1번째 인자가 false일때를 위한 부분 전문화 
template<typename T, typename U> 
struct IfThenElse<false, T, U>  {   
	typedef U ResultType; 
};   

// 크기가 더 큰 타입을 연산의 결과타입으로 지정 
// sizeof는 함수가 아니라 연산자이므로 compile-time에 결정이 가능하다. 
template <typename T, typename U> 
using ResultType = typename IfThenElse<(sizeof(T) > sizeof(U)), T, U>::ResultType;  

template <typename T, typename U> 
ResultType<U, V> Mul (const T& a, const U& b)  {  return a * b;  }

'NC University > Advanced C++' 카테고리의 다른 글

3일차 - 멤버함수 포인터  (0) 2015.07.03
2일차 - Int2Type & integral_constant  (0) 2015.07.01
2일차 - 템플릿 인자  (0) 2015.06.30
2일차 - value_type  (0) 2015.06.29
2일차 - 멤버함수 템플릿  (0) 2015.06.24