본문 바로가기
NC University/Advanced C++

1일차 - TypeTraits

by 날쑤 2021. 6. 14.

아래와 같은 코드를 생각해보자.

template<typename T> 
inline T Max(T a, T b) { return a < b ? b : a; }

int main(int argc, char* argv[])
{
    int x = 10, y = 20;
    
    Max(x, y);
    Max(&x, &y);
}

   Max 함수는 함수 템플릿이므로 위의 호출들은 아무 문제없이 수행될 것이다. 하지만 두번째 호출은 인자로 정수값의 주소를 넘기므로 T는 int* 로 추론될 것이며, 이 함수는 두 주소값을 비교한다. 이는 의미없는 행위이며, 실제로 사용자가 원하는 것은 아래와 같이 인자가 포인터일 경우에는 역참조한 값에 대한 비교일 것이다.

template<typename T> 
inline T Max(T a, T b)
{
    if (T is pointer-type)
        return *a < *b ? b : a;
    return a < b ? b : a;
}

  위의 함수에서 T가 pointer 타입인지를 판별하기 위해서 필요한 것이 TypeTraits이다.

  TypeTraits는 컴파일 시간에 타입에 대한 다양한 속성을 얻어내는 기술이다. 예를 들어 T가 포인터인지를 판별하는 TypeTrait는 다음과 같이 정의된다.

// primary template
template<typename T> struct IsPointer
{
    enum { kValue = false };
};

// partial specialization
template<typename T> struct IsPointer<T*>
{
    enum { kValue = true };
};

template<typename T> void foo(const T& a)
{
    if (IsPointer<T>::kValue)
        cout << "T: pointer" << endl;
    else
        cout << "T: not pointer" << endl;
} 

int main(int argc, char* argv[])
{
     int n = 0;
     
     foo(n);  //< T: not pointer
     foo(&n); //< T: pointer
     
     return 0;
}

   이제 컴파일 시간에 임의의 타입이 포인터 타입인지 아닌지를 판별할 수 있는 도구를 얻었다. 하지만 이것만으로는 위의 Max 함수가 완성되지 않는다. (실제로 컴파일 에러가 발생한다.) 이에 대해서는 다음에 다시 언급할 것이다.

  Pointer 타입을 판별하는 것 외에 TypeTraits 테크닉은 다양한 것들을 할 수 있는데, 이번에는 배열을 판별하는 Trait를 만들어보자.

// primary template
template<typename T> struct IsArray
{
    enum { kValue = false };
    enum { kSize = -1 };
};

// primary template의 인자가 한개라도 부분전문화 버전은 여러개가 가능하다.
template<typename T, int N> struct IsArray<T[N]>
{
    enum { kValue = true };
    enum { kSize = N };
};

template<typename T> void bar(const T& a)
{
    if (IsArray<t>::kValue)
        cout << "배열입니다. 크기는 " << IsArray<t>::kSize << endl;
    else
        cout << "배열이 아닙니다." << endl;
}

int main(int argc, char* argv[])
{
    int a = 0;
    int b[10] = {0};
    
    bar(a); //< 배열이 아닙니다.
    bar(b); //< 배열입니다. 크기는 10
    
    return 0;
}

 

도전과제: has_virtual_function<T>

  • 타입 T가 가상함수를 포함하고 있으면 kValue = true, 아니면 kValue = false
  • Hint: 어떤 타입 T가 가상함수를 포함하고 있으면, T에는 가상함수 테이블을 가리키는 포인터가 추가된다.