C++/[ecourse] C++ Template

9. 가변인자 템플릿-2

헛둘이 2022. 10. 7. 19:01
std::tuple 구현
  • tuple은 n개의 객체를 보관할 수 있는 템플릿 구조체
  • 때문에 가변인자 템플릿으로 만들어졌다.
#include <iostream>

template<typename ... Types> struct xtuple
{
	static constexpr int N = 0;
};

template<typename T, typename ... Types>
struct xtuple<T, Types...> : public xtuple<Types...>
{
	T value;

	xtuple() = default;
	xtuple(const T& v, const Types& ... args) 
		: value(v), xtuple<Types...>(args...) {}
	static constexpr int N = xtuple<Types...>::N + 1;
};

int main()
{
	xtuple<int, double, char> t3;
}
  • typename T, typename ... Types를 통해 맨 앞의 타입을 저장할 수 있음
  • 메인에서 3개의 타입을 넘겨주고 있는데 그럼 나머지 타입은?
  • xtuple<Types...> 상속을 통해 나머지 인자를 부모로 넘겨주고 있다.
  • xtuple<int, double, char>
  • xtuple<double, char>
  • xtuple<char> 이런 식..
  • int N은 N개의 인자를 가지고 있다는 의미
  • 그럼 나머지 값과 타입은 어디서 가지고 올까에 대한 내용은 이후 서술

 

 

 


타입 캐스팅 시 주의사항
  • 타입 캐스팅 시 값으로 캐스팅하면 인자로 넘어온 객체를 복사생성하여 임시객체를 만들고 그걸로 캐스팅
  • 임시객체를 만들지 않으려면 참조로 캐스팅해야 함
struct Base
{
	Base() = default;
   	Base( const Base& other) { std::cout << "base copy ctor"; }
	int value = 10;
}

struct Derived : public Base
{
	Derived() = default;
	int value = 20;
};

int main()
{
	Derived d;
	std::cout << d.value << std::endl;
	std::cout << d.Base::value << std::endl;

	std::cout << static_cast<Base>(d).value << std::endl;
	// 임시객체를 만드는데 d를 이용한 복사생성자를 통해 만듦

	std::cout << static_cast<Base&>(d).value << std::endl;

	static_cast<Base>(d).value = 30; // 값 캐스팅은 절대 사용하면 안됨, rvalue로 취급
	static_cast<Base&>(d).value = 30; // 참조로 캐스팅하므로 lvalue로 취급
}
  • 이와 마찬가지로 tuple에서 객체를 꺼낼 때 부모 클래스의 참조로 캐스팅해주면 됨
xtuple<int, double, char> t3(1, 3.4, 'A');
std::cout << static_cast<xtuple<double, char>&>(t3).value << std::endl;
std::cout << static_cast<xtuple<char>&>(t3).value << std::endl;
  • 이와 같이 호출하면 각각 3.4와 'A'가 출력되는 것을 볼 수 있다.

 


std::tuple_element 구현
template<size_t N, typename TP>
struct xtuple_element
{
	typedef TP type;
    // 이 부분은 필요 없으므로 선언만 두고 지워도 무방
};

// 요소의 타입을 구할 수 있도록 부분 특수화
template<typename T, typename ... Types>
struct xtuple_element<0, xtuple<T, Types...>>
{
	typedef T type;
};

template<size_t N, typename T, typename ... Types>
struct xtuple_element<N, xtuple<T, Types...>>
{
	typedef typename xtuple_element<N - 1, xtuple<Types...>>::type type;
};
  • tuple과 비슷하게 재귀를 돌며 N을 1씩 감소시킨 뒤 N이 1이 됐을 때 type을 출력한다.
  • 재귀를 돌 때마다 맨 앞의 타입은 T에 걸려 빠지게 되므로 결론적으로 N번째에 원하는 타입이 남게 됨

 


std::get 구현
template<size_t N, typename TP>
typename xtuple_element<N, TP>::type& xget(TP& tp)
{
	return static_cast<typename xtuple_element<N, TP>::tupleType&>(tp).value;
}
  • get은 위에서 구현했던 type_element를 통해 구현할 수 있게 되며,
  • 반환타입이 참조인 이유는 lvalue로 반환해줘야만 get을 한 결과에 쓸 수 있게되므로 참조로 하였음
  • 결과값에 캐스팅할 때 참조로 받는 이유도 위에 설명했던 것과 마찬가지
  • 주기적으로 머릿속에 그리면서 반복 숙달하는 과정 필요