반응형

is_array 예제

  • 간단히 템플릿 파라미터 T가 배열 타입 여부 확인 도구

  • 메인 템플릿(Main template)에서 false 반환( value = false )

  • 배열 타입 부분 특수화(Partial specialization)에서 true 반환( value = true )

  • 타입을 정확히 알아야 함

    • int x[3]; 에서 x는 변수 이름, 변수 이름을 제외한 나머지 요소(int[3])이 타입임

  • unknown size array type(T[])에 대해서도 부분 특수화가 필요함

#include <iostream>
#include <type_traits>
using namespace std;

template<typename T> struct is_array_custom
{
    static constexpr bool value = false;
};

// 크기를 알 수 있는 배열의 부분 특수화
template<typename T, size_t N>
struct is_array_custom<T[N]>
{
    static constexpr bool value = true;
};

// 크기를 알 수 없는 배열의 부분 특수화
template<typename T, size_t N>
struct is_array_custom<T[N]>
{
    static constexpr bool value = true;
};

template<typename T> void foo(T& a)
{
    // 크기를 알 수 있는 배열 확인
    if (is_array_custom<T>::value)
        cout << "array" << endl;
    else
        cout << "not array" << endl;
      
    // 크기를 알 수 없는 배열을 사용하는 패턴도 존재함
    //if (is_array_custom<int[]>::value)
}

int main()
{
    int x[3] = { 1, 2, 3 };
    foo(x);
}

 

is_array 배열 크기 구하기

  • 부분 특수화로 배열의 크기도 구할 수 있음

    • c++11 extent<T, 0>::value 존재

#include <iostream>
#include <type_traits>
using namespace std;

template<typename T> struct is_array_custom
{
    static constexpr bool value = false;
    static constexpr size_t size = -1;
};

//사이즈를 알 수 있는 배열에 대한 부분 특수화에서 N이 배열의 크기
template<typename T, size_t N>
struct is_array_custom<T[N]>
{
    static constexpr bool value = true;
    static constexpr size_t size = N;
};

template<typename T> void foo(T& a)
{
    if (is_array_custom<T>::value)
        cout << "size of array : " << is_array_custom<T>::size << endl;
}

int main()
{
    int x[3] = { 1, 2, 3 };
    foo(x);
}
반응형

반응형

type traits 개념

  • 컴파일 타임에 타입에 대한 정보를 얻거나 변형된 타입을 얻을때 사용하는 도구(메타 함수)

  • <type_traits> 헤더로 제공됨(c++11)

type query를 위한 type traits 만드는 방법

  • 메인 템플릿(Primary template)에서 false 반환( value = false )

  • 부분 특수화(Partial specialization)에서 true 반환( value = true )

 

is_pointer 예제

  • 간단히 템플릿 파라미터 T가 포인터 타입 여부 확인 도구

  • 메인 템플릿(Primary template)에서 false 반환( value = false )
  • 포인터 타입용 부분 특수화(Partial specialization)에서 true 반환( value = true )
#include <iostream>
#include <type_traits>
using namespace std;

template<typename T> struct is_pointer_custom
{
    enum { value = false }; 
};

// 포인터 타입에 대해서 부분 특수화 필요
template<typename T> struct is_pointer_custom<T*>
{
    enum { value = true };
};

template<typename T> void foo(T v)
{
    if (is_pointer_custom<T>::value)
        cout << "pointer" << endl;
    else
        cout << "not pointer" << endl;
}

int main()
{
    int n = 3;
    foo(n);
    foo(&n);
}

 

is_pointer 개선 예제

  • c++11 기준 코드 개선
  • 좀더 다양한 포인터 타입 지원(const, volatile, const volatile)
#include <iostream>
#include <type_traits>
using namespace std;

template<typename T> struct is_pointer_custom
{
    //enum { value = false }; 
    static constexpr bool value = false; // c++11
};

// 포인터 타입에 대해서 부분 특수화 필요
template<typename T> struct is_pointer_custom<T*>
{
    //enum { value = true };
    static constexpr bool value = true; // c++
};

// 포인터 타입에 대해서 부분 특수화 필요
template<typename T> struct is_pointer_custom<T* const>
{
    //enum { value = true };
    static constexpr bool value = true; // c++
};

// 포인터 타입에 대해서 부분 특수화 필요
template<typename T> struct is_pointer_custom<T* volatile>
{
    //enum { value = true };
    static constexpr bool value = true; // c++
};

// 포인터 타입에 대해서 부분 특수화 필요
template<typename T> struct is_pointer_custom<T* const volatile>
{
    //enum { value = true };
    static constexpr bool value = true; // c++
};

int main()
{
    cout << is_pointer_custom<int>::value << endl;
    cout << is_pointer_custom<int*>::value << endl;
    cout << is_pointer_custom<int* const>::value << endl;
    cout << is_pointer_custom<int* volatile>::value << endl;
    cout << is_pointer_custom<int* const volatile>::value << endl;
    cout << is_pointer_custom<int* volatile const>::value << endl;
}
반응형

반응형

Couple 

  • 2개의 인자로 받은 데이터를 보관하는 컨테이너

  • 인자로 Couple 타입도 전달 가능

  • 부분 특수화(Partial specialization)를 할때 파라미터 수를 잘 선택해야함

  • 파라미터로 자기 자신을 재귀로 입력 시 N 표현 방법 고려

  • 1번째 인자, 2번째 인자, 모든 인자가 Couple 일 경우 특수화 방법 고려

#include <iostream>
#include <type_traits>
using namespace std;

// 보관 데이터 수를 출력하는 함수
template<typename T> void printN(const T& cp)
{
    cout << T::N << endl;
}

// 2개의 인자를 보관하는 데이터(메인 템플릿)
template<typename T, typename U> struct Couple
{
    T v1;
    U v2;

    enum { N = 2 };
};

// 2번째 인자가 Couple일 경우
template<typename A, typename B, typename C>
struct Couple<A, Couple<B, C>>
{
    A v1;
    Couple<B, C> v2;
    enum { N = Couple<B, C>::N + 1 };
};

// 1번째 인자가 Couple일 경우
template<typename A, typename B, typename C>
struct Couple<Couple<A, B>, C>
{
    Couple<A, B> v1;
    C v2;
    enum { N = Couple<A, B>::N + 1 };
};

// 1번째, 2번째 모든 인자가 Couple일 경우
template<typename A, typename B, typename C, typename D>
struct Couple<Couple<A, B>, Couple<C, D>>
{
    Couple<A, B> v1;
    Couple<C, D> v2; 
    enum { N = Couple<A, B>::N + Couple<C, D>::N };
};

int main()
{
    // 2개의 인자 데이터
    Couple<int, double> c2;
    
    // 2번째 인자에 Couple 데이터를 입력 할 경우(총 3개 보관)
    // 가변 영역의 데이터는 int, int, char 총 3개
    Couple<int, Couple<int, char>> c3;
    
    // 2번째 인자의 Couple 데이터의 2번째 인자에 다시 Couple 데이터를 입력 할 경우(총 4개 보관)
    // Couple 데이터를 중첩하여 사용하였으나 가변 영역은 동일하게 int, int, T로 총 3개
    Couple<int, Couple<int, Couple<int, char>>> c4;
    
    // 1번째, 2번째 인자 모두 Couple 데이터를 입력 할 경우(총 4개 보관)
    Couple<Couple<int, int>, Couple<int, int>> c5;

    // 보관 데이터 수 출력
    printN(c2);
    printN(c3);
    printN(c4);
    printN(c5);
}

 

반응형

반응형

IfThenElse 예제

  • 컴파일 시간 bool값에 따라 type을 선택하는 도구
  • 일반적으로 IfThenElse, IF, Select 등 이름으로 구현함
  • C++ 표준에는 conditional 이름으로 제공<type_traits> 헤더
#include <iostream>
using namespace std;

// IfThenElse 메인 템플릿
template<bool b, typename T, typename F> struct IfThenElse
{
    typedef T type;
};

// 부분 특수화
template<typename T, typename F> 
struct IfThenElse<false, T, F>
{
    typedef F type;
};

int main()
{
    // 첫번째 파라미터를 true로 할 경우 T 반환
    IfThenElse<true, int, double>::type t0; // int
    
    // 첫번째 파라미터를 false로 셋팅할 경우 F 반환
    IfThenElse<false, int, double>::type t1; // double

    cout << typeid(t0).name() << endl;
    cout << typeid(t1).name() << endl;
}

 

conditional 활용

#include <iostream>
#include <type_traits>
using namespace std;

template<size_t N> struct Bit
{
    // bit 단위로 데이터 관리가 필요할 경우 조건별 타입을 사용할 수 있음
    // 8이하 : char, 16이하 : short, 32이하 : int...
    using type = typename conditional<(N <= 8), char, 
        typename conditional<(N <= 16), short, int>::type>::type;

    type bitmap;
};

int main()
{
    Bit<32> b1;
    Bit<8> b2;
    Bit<16> b3;

    cout << sizeof(b1) << endl;
    cout << sizeof(b2) << endl;
    cout << sizeof(b3) << endl;
}

 

반응형

반응형

템플릿 특수화(Template specialization), 부분 특수화(Partial specialization) 개념

  • 템플릿의 특정 패턴에 대해서 별도의 처리가 하고 싶을 경우 부분 특수화 또는 특수화를 이용할 수 있음
  • 메인 템플릿의 인자가 2개라면, 사용자는 반드시 템플릿 인자를 2개 전달해야 한다.(기본값이 없다면)
  • 부분 특수화(Partial specialization) 템플릿에서는 인자의 수는 메인 템플릿 인자수와 다를 수 있다.
  • 특수화를 많이 할 경우 소스코드가 늘어날 수는 있지만 결국 목적코드로 생성되는 기계어 코드의 양은 같음
#include <iostream>
using namespace std;

// 메인 템플릿(Primary template)
// 기본적으로 모든 패턴은 메인 템플릿이 처리함
template<typename T> class stack
{
public:
    void push(T a) { cout << "T" << endl; }
};

// 부분 특수화(Partial specialization)
// 모든 포인터 타입에 대해서 별도로 처리하고 싶을 경우 부분 특수화를 적용할 수 있음
template<typename T> class stack<T*>
{
public:
    void push(T* a) { cout << "T*" << endl; }
};

// 특수화(Speicalization)
// char포인터에 대해서만 별도로 처리하고 싶을 경우 특수화를 적용할 수 있음
template<> class stack<char*>
{
public:
    void push(char* a) { cout << "char*" << endl; }
};


int main()
{
    stack<int> s1; s1.push(0); // 메인 템플릿 사용
    stack<int*> s2; s2.push(0); // 부분 특수화 템플릿 사용
    stack<char*> s3; s3.push(0); // 특수화 템플릿 사용
}

 

템플릿 특수화(specialization)/부분 특수화(Partial specialization) 예제

#include <iostream>
using namespace std;

// 1. <T, U> 메인 템플릿
template<typename T, typename U> class test
{
public:
    static void foo() { cout << "T, U" << endl; }
};

// 2. <T*, U> 부분 특수화
template<typename T, typename U> class test<T*, U>
{
public:
    static void foo() { cout << "T*, U" << endl; }
};

// 3. <T*, U*> 부분 특수화
template<typename T, typename U> class test<T*, U*>
{
public:
    static void foo() { cout << "T*, U*" << endl; }
};

// 4. <T, T> 부분 특수화
template<typename T> class test<T, T>
{
public:
    static void foo() { cout << "T, T" << endl; }
};

// 5. int, T
template<typename T> class test<int, T>
{
public:
    static void foo() { cout << "int, T" << endl; }
};

// 5. <int, T> 부분 특수화
template<> class test<int, int>
{
public:
    static void foo() { cout << "int, int" << endl; }
};

// 6. <int, short> 특수화
template<> class test<int, short>
{
public:
    static void foo() { cout << "int, short" << endl; }
};

// 7. T, test<U, V> 부분 특수화
template<typename T, typename U, typename V> 
class test<T, test<U, V>>
{
public:
    static void foo() { cout << "T, test<U, V>" << endl; }
};

int main()
{
    // 1. <T, U> 메인 템플릿
    test<int, double>::foo();

    // 2. <T*, U> 부분 특수화
    test<int*, double>::foo();

    // 3. <T*, U*> 부분 특수화
    test<int*, double*>::foo(); 

    // 4. <T, T> 부분 특수화
    test<int, int>::foo();

    // 5. <int, T> 부분 특수화
    // 인자가 <int, int>일 경우 4.<T, T> 5.<int, T> 패턴이 2개이상 일치하므로 5-1 특수화 추가 구현 필요
    test<int, char>::foo(); 

    // 6. <int, short> 특수화
    test<int, short>::foo(); 

    // 7. T, test<U, V> 부분 특수화
    test<double, test<char, short>>::foo();
}

 

템플릿 특수화(specialization)/부분 특수화(Partial specialization) 주의사항

  • 부분 특수화에서 T의 타입이 결정되는 방식을 주의해야 함
  • 부분 특수화에서 기본 파라미터는 표시하지 않음(메인 템플릿에 정의 값을 그대로 승계함)
  • 클래스의 특정 멤버 함수만 특수화 할 수 있으나 부분 특수화는 불가능
// 부분 특수화 시 T 타입의 주의
// 메인 템플릿에 포인터값이 넘어 왔을 경우 T는 포인터 타입임
template<typename T> class Test
{
public:
    static void foo() { cout << typeid(T).name() << endl; }
};

// 아래와 같이 포인터 타입으로 부분 특수화를 했을 경우 T는 포인터 타입이 아님
template<typename T> class Test<T*>
{
public:
    static void foo() { cout << typeid(T).name() << endl; }
};
// 메인 템플릿에 정의된 파라미터 기본 파라미터
template<typename T, int N = 10> class Test
{
public:
    static void foo() { cout << typeid(T).name() << endl; }
};

// 부분 특수화에서는 기본 파라미터를 표시하지 않음(메인 템플릿의 기본 파라미터 값 승계)
template<typename T, int N> class Test<T*, N>
{
public:
    static void foo() { cout << typeid(T).name() << endl; }
};
// 클래스 템플릿
template<typename T>
class Stack
{
public:
    T pop() {}
    void push(T a);
};

// 특정 멤버 함수의 특수화가 필요할 경우 구현부를 분리
template<typename T>
void Stack<T>::push(T a)
{
    cout << "T" << endl;
}

// 특정 멤버 함수의 특수화 구현(부분 특수화는 불가능함)
template<>
void Stack<char*>::push(char* a)
{
    cout << "char*" << endl;
}

int main()
{
    Stack<int> s1; s1.push(0);
    Stack<char*> s2; s2.push(0);
}

 

반응형

+ Recent posts