프로그래밍/C/C++

C++ template 과 type 추론

nanze 2022. 1. 11. 21:16
반응형

C++ template 과 type 추론

이번 포스팅 정리는 template 과 type 추론 관련해서 정리해 본다. 보통 우리는 최소값 구하는 함수를 아래와 같이 구현할지 모른다. 

#include <iostream>

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

int main()
{
    int x = 1;
    int y = 2;
    
    GetMin(x, y);
    GetMin(&x, &y);
    
    return 0;
}

위 코드에서 두번째 호출되는 GetMin 은 애매모호 하다. 어떤 것이 옳다고 볼 수 없기 때문이다. 값을 비교하려는 것인지 주소값을 비교하려는 것인지. 본래 의도라면 해당 변수에 있는 값을 비교해야 했으리라. 그럼 어떻게 접근해야 할까?

T 의 타입이 어떤 타입인지 판단한 후 판단 결과에 맞게 동작하면 되는 것으로 type 추론이 필요한 것이다. 

다음 코드를 보자.

#include <iostream>

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

template <typename T> struct PointType<T*>{
    enum { value = true };
}

template <typename T>
void IsPointer(T a, T b)
{
    if(PointType<T>::value){
        wcout << "this is pointer type" << endl;
    }
    else{
        wcout << "this is not pointer type " <<endl; 
    }
}

int main()
{
    int x = 1;
    int y = 2;
    
    IsPointer(x, y);
    IsPointer(&x, &y);
    
    return 0;
}

위 코드로 포인터 타입인지는 알 수 있으나 들어온 여러 인자에 대해서 판단하고 값을 반환하지는 못 한다. 완벽한 최소값 함수를 구현하고자 한다면 integral_constant 개념도 넣어야 한다. 이것은 나중 포스팅에서 정리하도록 하겠다. 

 

type 추론 관련해서 다른 예를 보도록 해보겠다.

다음 코드를 보도록 하자. 아래 코드의 예는 가상함수를 판별하는 예시이다. 

#include <iostream>

using namespace std;

template <typename T>
class vtable{
public:
    class child : public T
    {
        virtual void vfunc(){}
    };
    enum { value = sizeof(T) == sizeof(child) };
};

template <typename T>
void hasVtable(T& a)
{
    if(vtable<T>::value){
        cout << "has virtual table." << endl;
    }else{
        cout << "has not virtual table." << endl;
    }
}

class test
{
public:
    int a;
    virtual void vfunc() {}
};

class test2
{
public:
    int a;
    void func() {}
};

int main()
{
    test a;
    test2 b;
    
    hasVtable(a);
    hasVtable(b);
    
    return 0;
}

위 코드는 들어온 T 타입의 클래스를 상속받은 후 자식 클래스에 가상함수를 만들어 그 크기를 부모와 비교하는 것이다. 만약 부모가 가상 함수를 가지고 있다면 가상 함수 테이블 크기 때문에 자식과 같을 것이고 그렇지 않다면 자식 클래스 크기가 클 것이다. 이것을 이용한 것이다. 

 

예시 하나만 더 보도록 하자. 다음 예제는 들어온 인자가 배열인지 판별하는 코드이다. 

#include <iostream>

using namespace std;

template <typename T>
struct isArray
{
    enum { value = false};
    enum { size = -1 };
};

template <typename T, int N>
struct isArray<T[N]>
{
    enum { value = true};
    enum { size = N};
};

template <typename T>
void IsArray(const T& a)
{
    if( isArray<T>::value){
        wcout << "T is array. size " <<  isArray<T>::size << endl;
    }else{
        wcout << "T is not array." << endl;
    }
}

int main()
{
    int x = 0 ;
    int y[5] = {0x00,};
    
    IsArray(x);
    IsArray(y);

    return 0;
}

보통 template type 추론에서는 template 부분 특수화가 많이 사용되는 것 같다. 메인 템플릿 형태로부터 타입에 맞는 부분 특수화를 받아들여 플래그를 설정하는 그런 방식이 사용된다. std 에서는 이러한 type 추론을 많이 지원하고 있다. 

아래는 몇가지 예이다. 

std::is_pointer
std::is_member_pointer
std::is_member_object_pointer
std::is_array
std::is_scalar
반응형