본문 바로가기

Library/C/C++

템플릿 클래스에서의 템플릿 멤버 함수에 대한 템플릿 특수화

가끔, 템플릿 클래스에서 템플릿 멤버 함수를 쓸 일이 있다. 어떤 자료형 T에 대해 인스턴스화된 클래스에서, 다른 자료형 U를 인자(Argument)로 요구하는 멤버 함수가 필요할 때가 있는데, 이것은 설사 U가 T와 같은 자료형이라고 하더라도 컴파일 문제 때문에 이렇게 해야 할 일이 있다. 다음의 코드를 보면,


template< typename T >
class Base
{
protected:
    T _value;

public:
    void SetValue(T value) { _value = value; }
}


SetValue()는 동일한 T 자료형을 받아서 자신의 데이터 멤버에 대입하는 함수이므로, 아무런 문제가 없다. 그러나, 만약 T가 이런 방식의 연산자 오버로딩을 제공하지 않는다면 이 코드는 컴파일에 실패한다. 예를 들어, SetValue()가 char* 타입을 인자로 받아서 이것을 double 타입으로 변환하여 저장한다고 해보자.


template<
void Base< T >::SetValue(const char* value)
{
    _value = std::atof(value);
}


기본형에 대해서라면, 이 코드는 문제 없는 코드이다. 그러나, Base를 템플릿 클래스로 만들었다면, _value가 T 타입으로 인스턴스화되고, SetValue()로 이 값을 설정하기를 바랬을 것이다. 만약, T가 임의의 Test 클래스라고 하고, 이 클래스는 double 타입으로의 형변환 연산자를 오버로딩(overloading) 하지 않았다면 이 코드는 컴파일에 실패한다. 이것은 애초에 Base 클래스를 템플릿 클래스로 만든 목적에 비추어볼 때, 상당히 심각한 문제이다. 즉, T 타입 모두가 = 연산자를 이런 방식으로 사용하는 것이 아니기 때문에, T 타입에 따라 컴파일에 실패할 수 있다. 이 문제를 해결하려면, 템플릿 멤버 함수를 통한 템플릿 특수화를 사용해야 한다.

템플릿 함수는 인스턴스화 되지 않는다면 문법의 적법성만 검사되며, 실제로 호출되지 않는다. 따라서, Test 클래스에 특수화된 템플릿 멤버 함수를 제공한다면 위의 코드 대신에 특수화된 템플릿 멤버 함수가 호출될 것이며, 템플릿은 인스턴스 시점에서 틀이 만들어진다. 따라서, Test 타입에 대한 정의는 특수화된 템플릿을 사용하며, 특수화되지 않은 템플릿 코드에서의 _value = value 구문은 무시되고 컴파일에 성공할 수 있다. 만약, 템플릿 멤버 함수를 쓰지 않는다면 함수 오버로딩을 생각할 수 있는데, 함수를 오버로딩 하더라도 실제 인스턴스화된 Base 클래스의 _value 타입과 호출된 멤버 함수의 인자가 일치한다고 할 수 없기 때문에 완벽한 해결책은 아니다.

즉, SetValue()를 템플릿 함수로 정의한 뒤 _value를 초기화하는 코드를 포함한, 특수화된 템플릿을 제공하면 문제는 깨끗하게 해결된다. 예를 들어, Test 클래스는 = 연산자에 대해 문자열을 인자로 받고 적절한 초기화를 한다고 하자. 그렇다면, 위의 일반적인 정의에서는 _value = std::atof(value)는 T가 double 타입이라면 적법한 코드이다. 그러나, T가 Test 클래스라면 = 연산자가 double 타입에 대한 오버로딩된 함수를 제공하지 않는 이상 컴파일에 실패한다. Test 클래스에 연산자 오버로딩을 추가하는 것이 쉽지 않다면, 다음과 같은 템플릿 특수화 코드를 사용할 수 있다.


template< typename T >
class Base
{
protected:
    T _value;

public:
    template< typename U > void SetValue(U value) { _value = value; }
    // 이것은 일반적으로 사용되는 템플릿 함수이며,
    // 특별한 초기화 방법을 제공하지 않는 타입은 모두 이것을 사용하게 된다.
};

template<>
    template<>
    void Base< Test >::SetValue< Test >(const char* value)
    {
        _value = value
        // 이제 Test 타입에 대한 SetValue()는 이 템플릿 특수화 함수가 호출되며, Test 클래스는
        // = 연산자에 대해 문자열을 인자로 받기 때문에 컴파일이 가능하다.
    }


여기서, 위의 코드를 다음과 같이 쓰고 싶을 것이다.


template< typename T >
    tempalate<>
    void Base< T >::SetValue< Test >(Test value)
....


이 코드는 컴파일에 실패하는데, 그 이유는 명시적인 템플릿 특수화에 있어서, 이 시점에서 Base< T >는 존재하지 않기 때문이다. 따라서, 약간의 불편함을 감수하고서라도 어떤 타입에 대한 템플릿 특수화인지 모두 적어주어야 한다.