leesangwon0114

I am Research Engineer. Currently working in KT.

C++Template 21. variadic Template활용#2

26 Aug 2018 » c++

codenuri 강석민 강사 강의 내용기반으로 정리한 내용입니다.

get using variadic template #1

기반 클래스에 멤버에 접근 하는 방법
  1. 기반 클래스의 멤버와 파생클래스의 멤버의 이름이 동일 할 때는 자신(파생클래스)의 멤버가 우선시 된다.
  2. 기반 클래스의 멤버에 접근하는 방법
    • d.Base::value
    • static_cast<Base&>(d).value;
  3. 값 캐스팅과 참조 캐스팅
    • static_cast(d): 임시 객체 생성 lvalue 가 될 수 없다.
    • static_case<Base&>(d): 임시객체를 생성 안함. lvalue 가 될 수 있다.

#include <iostream>
using namespace std;

struct Base
{
	int value = 10;
}

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

int main()
{
	Derived d;

	cout << d.value << endl;	// 20
	cout << d.Base::value << endl; // 10

	cout << static_cast<Base>(d).value << endl; // 10 -> 임시 객체의 10임
	cout << static_cast<Base&>(d).value << endl; // 10

	static_cast<Base>(d).value = 30; // error (lvalue 가 될 수 없음)
	static_cast<Base&>(d).value = 30; // ok
}


tuple의 요소에 접근하는 get 만들기
  1. tuple 이 0번째 요소는 자기 자신이 보관한다.
  2. tuple 의 N번째 요소는 N번째 기반 클래스가 저장 한다.
  3. N번째 요소의 타입과 N번째 요소를 저장하는 타입(N번째 기반 클래스 타입)을 알아야 한다.
#include <iostream>
using namespace std;

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(1, 3.4, 'A');

	cout << t3.value << endl; // 1
	// cout << static_cast<기반클래스&>(t3).value << endl;
	cout << static_cast<xtuple<double, char>&>(t3).value << endl; // 3.4
	cout << static_cast<xtuple<char>&>(t3).value << endl; // A

	char c = xget<2>(t3);
}

template<size_t N, typename TP> // TP는 튜플
튜플TP N번째 요소의 타입&
xget(TP& tp)
{
	return static_cast<TP N번째 기반클래스&>(tp).value;
}


get using variadic template #2


int main()
{
	tuple_element<1, tuple<int, double, char>>::type n;
	cout << typeid(n).name() << endl;
}


tuple_element

1.tuple이 가진 N 번째 요소의 타입을 구하는 템플릿

2.primary template 을 만든다. -> 의도적으로 선언만 할 수도 있다.

3.0번째 요소를 구하는 부분 특수화를 만든다.

#include <iostream>
using namespace std;

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;
}

template<size_t N, typename TP> struct xtuple_element
{
	typedef TP type;
}
// 요소의 타입을 구할 수 있도록 부분 특수화를 제공한다.
template<typenmae T, typename ... Types> struct xtuple_element<0, xtuple<T, Types...>>
{
	typedef T type;
}

int main()
{
	xtuple_element<0, xtuple<int, double, char>>::type n;
	cout << typeid(n).name() << endl;
}

int 출력되는 것 확인가능함


4.N번째 요소를 위한 부분 특수화를 만들고, Recursive를 사용한다.

#include <iostream>
using namespace std;

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;
}

template<size_t N, typename TP> struct xtuple_element;

// 요소의 타입을 구할 수 있도록 부분 특수화를 제공한다.
template<typenmae T, typename ... Types> struct xtuple_element<0, xtuple<T, Types...>>
{
	typedef T type;
}
template<size_t N, typenmae T, typename ... Types> struct xtuple_element<N, xtuple<T, Types...>>
{
	typedef typename xtuple_element<N-1, xtuple<Types...>>::type type;
}

int main()
{
	xtuple_element<2, xtuple<int, double, char>>::type n;
	cout << typeid(n).name() << endl;
}

char 출력됨


최종적으로 get 만들기
#include <iostream>
using namespace std;

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;
}

template<size_t N, typename TP> struct xtuple_element;

// 요소의 타입을 구할 수 있도록 부분 특수화를 제공한다.
template<typenmae T, typename ... Types> struct xtuple_element<0, xtuple<T, Types...>>
{
	typedef T type; // 0번째 요소의 타입
	typedef xtuple<T, Types...> tupleType; // 0번째 요소를 저장하는 타입
}
template<size_t N, typenmae T, typename ... Types> struct xtuple_element<N, xtuple<T, Types...>>
{
	typedef typename xtuple_element<N-1, xtuple<Types...>>::type type;
	typedef typename xtuple_element<N-1, xtuple<Types...>>::tupleType tupleType;
}

template<size_t N, typename TP>
typename xtuple_element<N, TP>::type& xget(TP& tp)
{
	return static_cast<typename xtuple_element<N, TP>::tupeType&>(tp).value;
}

int main()
{
	xtuple<int, double, char> t3(1, 3.4, 'A'); // 0번째 요소의 타입 : int, 0번째 요소를 저장하는 타입 : xtuple<int, double, char>

	// xtuple_element<2, xtuple<int, double, char>>::type n; // char
	// xtuple_element<2, xtuple<int, double, char>>::tupleType t; // tuple<char>

	xget<1>(t3) = 1.1;

	cout << xget<1>(t3) << endl; // 1.1
}


#include <iostream>
#include <tuple>

using namespace std;

int main()
{
	tuple<int, int, int> tp(1,2,3);

	print_tuple(tp); // 1,2,3
}


int x[] = {get<0>(tp), get<1>(tp)}; // get이 tuple의 개수에 따라…(get 은 template 이여서 컴파일 타임 상수로 해결해야함) -> index_sequence

#include <iostream>
#include <tuple>

using namespace std;

template<size_t ... N> struct index_sequence{};

/*
template<typename TP> void print_tuple(const TP& tp)
{
	int x[] = {get<0>(tp), get<1>(tp)}; // get이 tuple의 개수에 따라...(get 은 template 이여서 컴파일 타임 상수로 해결해야함)

	for(auto& n : x)
		cout << n << ", ";

}
*/

template<typename TP, size_t ... I> void print_tuple(const TP& tp, index_sequence<I>& ) // I: 0, 1, 2
{
	int x[] = {get<I>(tp)...}; // get<0>(tp), get<1>(tp), get<2>(tp)

	for(auto& n : x)
		cout << n << ", ";

}

int main()
{
	tuple<int, int, int> tp(1,2,3);

	print_tuple(tp, index_sequence<0,1,2>()); // 1,2,3
}


사용할 때 매번 index_sequence 넘겨야 하는 불편함…

C++11 make sequence 존재 -> make_index_sequence

#include <iostream>
#include <tuple>

using namespace std;

// template<size_t ... N> struct index_sequence{};

template<typename TP, size_t ... I> void print_tuple_imp(const TP& tp, index_sequence<I>& ) // I: 0, 1, 2
{
	int x[] = {get<I>(tp)...}; // get<0>(tp), get<1>(tp), get<2>(tp)

	for(auto& n : x)
		cout << n << ", ";

}

template<typename TP> void print_tuple(const TP& tp) // I: 0, 1, 2
{
	print_tuple_imp(tp, make_index_sequence<tuple_size<TP>::value>());
}


int main()
{
	tuple<int, int, int> tp(1,2,3);

	// print_tuple(tp, make_index_sequence<3>()); // 1,2,3
	print_tuple(tp);
}

컴파일타임 상수를 가변인자로 받아낼 수 있는 것이 핵심!