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을 한 결과에 쓸 수 있게되므로 참조로 하였음
- 결과값에 캐스팅할 때 참조로 받는 이유도 위에 설명했던 것과 마찬가지
- 주기적으로 머릿속에 그리면서 반복 숙달하는 과정 필요