KoreanFoodie's Study
C++ 기초 개념 9-4 : 템플릿 메타 프로그래밍 2 (의존 타입, auto) 본문
모두의 코드를 참고하여 핵심 내용을 간추리고 있습니다. 자세한 내용은 모두의 코드의 씹어먹는 C++ 강좌를 참고해 주세요!
TMP를 이용한 소수 판별 프로그램
컴파일러는 구조상 어떠한 식별자를 보았을때 '값'인지 '타입'인지 결정을 해야 한다. 다음의 예시를 보자.
template <typename T>
int func() {
T::t* p;
}
class A {
const static int t;
};
class B {
using t = int;
};
위에서, 클래스 A 에 대해 func 함수를 특수화한다면, t 가 어떠한 int 값이 되어 T::t* p; 가 단순히 클래스 A 의 t 와 p 를 곱하는 식으로 해석이 된다.
반면, func 함수가 클래스 B 에 대해 특수화 된다면, T::t* p; 는 int 형 포인터 p 를 선언하는 꼴이 된다. 따라서 컴파일러는 이 두 상황을 명확히 구분하기 위해 T::t 가 타입인지 아니면 값인지 명확하게 알려줘야만 한다.
이렇게 템플릿 인자에 따라 어떠한 타입이 달라질 수 있는 것을 의존 타입(dependent type) 이라고 부른다. 따라서 컴파일러가 문장을 성공적으로 해석하기 위해서는 반드시 우리가 쓸 T 가 타입인지 값인지 알려줘야 한다. 타입임을 명시할 때는 typename 을 붙여주기만 하면 된다! (값일 경우 필요없음. 컴파일러는 어떤 식별자를 보았을 때 기본적으로 '값'이라고 생각하기 때문)
이제 완성된 소수 판별 프로그램 코드를 보자.
#include <iostream>
template <int N>
struct INT {
static const int num = N;
};
template <typename a, typename b>
struct add {
typedef INT<a::num + b::num> result;
};
template <typename a, typename b>
struct divide {
typedef INT<a::num / b::num> result;
};
using one = INT<1>;
using two = INT<2>;
using three = INT<3>;
template <typename N, typename d>
struct check_div {
// result 중에서 한 개라도 true 면 전체가 true
static const bool result = (N::num % d::num == 0) ||
check_div<N, typename add<d, one>::result>::result;
};
template <typename N>
struct _is_prime {
static const bool result = !check_div<N, two>::result;
};
template <>
struct _is_prime<two> {
static const bool result = true;
};
template <>
struct _is_prime<three> {
static const bool result = true;
};
template <typename N>
struct check_div<N, typename divide<N, two>::result> {
static const bool result = (N::num % (N::num / 2) == 0);
};
template <int N>
struct is_prime {
static const bool result = _is_prime<INT<N>>::result;
};
int main() {
std::cout << std::boolalpha;
std::cout << "Is 2 prime ? :: " << is_prime<2>::result << std::endl;
std::cout << "Is 10 prime ? :: " << is_prime<10>::result << std::endl;
std::cout << "Is 11 prime ? :: " << is_prime<11>::result << std::endl;
std::cout << "Is 61 prime ? :: " << is_prime<61>::result << std::endl;
}
의존 타입 문제가 생기는 경우는 다음과 같다 :
template <typename N, typename d>
struct check_div {
// result 중에서 한 개라도 true 면 전체가 true
static const bool result = (N::num % d::num == 0) ||
check_div<N, typename add<d, one>::result>::result;
};
위 코드에서, check_div 부분이 바로 typename 을 명시함으로써 의존 타입 문제를 해결한 경우이다!
단위(Unit) 라이브러리
TMP 를 활용하면, 단위 연산을 컴파일 타임에 에러 없이 구현할 수 있다. 즉, 단위가 다른 값 끼리는 연산이 애초에 호환되지 않도록 강제할 수 있다는 뜻이다. 코드를 통해 알아보자.
#include <iostream>
#include <typeinfo>
template <int X, int Y>
struct GCD {
static const int value = GCD<Y, X % Y>::value;
};
template <int X>
struct GCD<X, 0> {
static const int value = X;
};
template <int N, int D = 1>
struct Ratio {
private:
const static int _gcd = GCD<N, D>::value;
public:
typedef Ratio<N / _gcd, D / _gcd> type;
static const int num = N / _gcd;
static const int den = D / _gcd;
};
template <class R1, class R2>
struct _Ratio_add {
using type = Ratio<R1::num * R2::den + R2::num * R1::den, R1::den * R2::den>;
};
template <class R1, class R2>
struct Ratio_add : _Ratio_add<R1, R2>::type {};
template <class R1, class R2>
struct _Ratio_subtract {
using type = Ratio<R1::num * R2::den - R2::num * R1::den, R1::den * R2::den>;
};
template <class R1, class R2>
struct Ratio_subtract : _Ratio_subtract<R1, R2>::type {};
template <class R1, class R2>
struct _Ratio_multiply {
using type = Ratio<R1::num * R2::num, R1::den * R2::den>;
};
template <class R1, class R2>
struct Ratio_multiply : _Ratio_multiply<R1, R2>::type {};
template <class R1, class R2>
struct _Ratio_divide {
using type = Ratio<R1::num * R2::den, R1::den * R2::num>;
};
template <class R1, class R2>
struct Ratio_divide : _Ratio_divide<R1, R2>::type {};
template <typename U, typename V, typename W>
struct Dim {
using M = U;
using L = V;
using T = W;
using type = Dim<M, L, T>;
};
template <typename U, typename V>
struct add_dim_ {
typedef Dim<typename Ratio_add<typename U::M, typename V::M>::type,
typename Ratio_add<typename U::L, typename V::L>::type,
typename Ratio_add<typename U::T, typename V::T>::type>
type;
};
template <typename U, typename V>
struct subtract_dim_ {
typedef Dim<typename Ratio_subtract<typename U::M, typename V::M>::type,
typename Ratio_subtract<typename U::L, typename V::L>::type,
typename Ratio_subtract<typename U::T, typename V::T>::type>
type;
};
template <typename T, typename D>
struct quantity {
T q;
using dim_type = D;
quantity operator+(quantity<T, D> quant) {
return quantity<T, D>(q + quant.q);
}
quantity operator-(quantity<T, D> quant) {
return quantity<T, D>(q - quant.q);
}
template <typename D2>
quantity<T, typename add_dim_<D, D2>::type> operator*(quantity<T, D2> quant) {
return quantity<T, typename add_dim_<D, D2>::type>(q * quant.q);
}
template <typename D2>
quantity<T, typename subtract_dim_<D, D2>::type> operator/(
quantity<T, D2> quant) {
return quantity<T, typename subtract_dim_<D, D2>::type>(q / quant.q);
}
// Scalar multiplication and division
quantity<T, D> operator*(T scalar) { return quantity<T, D>(q * scalar); }
quantity<T, D> operator/(T scalar) { return quantity<T, D>(q / scalar); }
quantity(T q) : q(q) {}
};
template <typename T, typename D>
std::ostream& operator<<(std::ostream& out, const quantity<T, D>& q) {
out << q.q << "kg^" << D::M::num / D::M::den << "m^" << D::L::num / D::L::den
<< "s^" << D::T::num / D::T::den;
return out;
}
int main() {
using one = Ratio<1, 1>;
using zero = Ratio<0, 1>;
quantity<double, Dim<one, zero, zero>> kg(2);
quantity<double, Dim<zero, one, zero>> meter(3);
quantity<double, Dim<zero, zero, one>> second(1);
// F 의 타입은 굳이 알필요 없다!
auto F = kg * meter / (second * second);
std::cout << "2 kg 물체를 3m/s^2 의 가속도로 밀기 위한 힘의 크기는? " << F
<< std::endl;
}
위에서 auto 키워드를 통해 자동으로 타입을 추측하게 만들어 코드가 더욱 간결해졌다. auto 타입을 추론하는 방법은 템플릿에 들어갈 타입을 추론하는 것과 동일하다!
'Tutorials > C++ : Beginner' 카테고리의 다른 글
C++ 기초 개념 10-2 : 맵(map), 셋(set), unordered_map, unordered-set (0) | 2022.03.18 |
---|---|
C++ 기초 개념 10-1 : 벡터(vector), 리스트(list), 덱(deque) (0) | 2022.03.18 |
C++ 기초 개념 9-3 : 템플릿 메타 프로그래밍 (0) | 2022.01.16 |
C++ 기초 개념 9-2 : 가변 길이 템플릿(variadic template), 파라미터 팩(parameter pack), Fold 형식(Fold expression) (0) | 2022.01.14 |
C++ 기초 개념 9-1 : C++ 템플릿, 함수 객체(Functor) (0) | 2022.01.12 |