배움 저장소

[C++] Template 본문

Programming Language/C++

[C++] Template

시옷지읏 2021. 11. 13. 11:35

Overloading 대신에 Template 사용하기

 정수, 문자열을 출력하는 함수를 만들어보자. 같은 함수명이되 다른 자료형을 가지고 있는 함수를 Overloading해주면 된다.

void PrintAny(int i)
{ cout << i << endl; }

void PrintAny(float f)
{ cout << f << endl; }

void PrintAny(string s)
{ cout << s << endl; }

 자료형이 하나일 때는 괜찮지만 set, vector, qeueue, dequeue 같이 STL을 사용하고 자료형이 다양해지면 Overloading을 일일이 다 해주어야한다. 이럴 때 간편하게 사용할 수 있는 Template가 있다. 위 예제는 다음과 같이 쓸 수 있다.

template<typename T>
void printAny(T value)
{ cout << value << endl; }

int main()
{
    printAny<int>(5);
    printAny<float>(5.0);
    printAny<string>("Hello");
    
    printAny(5);
    printAny(5.0);
    printAny("Hello");	
}

 사용법은 함수명<사용할 자료형>(자료형에 맞는 입력값) 형식이다. 이 때 사용할 자료형은 입력값에서 알 수 있으므로 생략해주어도 관계가 없다. 

 typename은 함수를 정의할 때 사용하는 매개변수(Parameter)처럼 여러 자료형을 담을 수 있다. class로 바꾸어 사용하여도 된다. 하지만 class가 사용자가 필요로 하는 자료형을 직접 만들 때 사용한다는 점을 고려하면 template를 사용할 때는 typename을 사용함이 좋아보인다.

 

(이하 코드는 MSVC에서 실행되었다. 컴파일러에 따라 다른 결과가 나올 수 있다.)

 이때 Template 이하에서 작성된 코드는 함수가 호출되어야만 소스코드가 만들어진다. 아래 printAny 함수를 보면 이전에 선언된 적이 없는 'v'를 사용하고 있다. 컴파일 했을 때 해당 파일은 에러가 나야하지만 

template<typename T>
void printAny(T value)
{ cout << v << endl; }

int main()
{
    cout << "Is it fine?\n" << endl;
    return 0;
}

위 코드는 아무 문제없이 실행됨을 알 수 있다. Template를 지우고 다시 호출해보자.

void printAny(int value)
{ cout << v << endl; }

int main()
{
    cout << "Is it fine?\n" << endl;
    return 0;
}

위 예제는 컴파일을 시도하면 실패할 것이다. Template를 이용한 코드는 해당 코드가 호출될 시점에 소스코드를 만들기 때문이다. 해당 함수가 호출 될 때 typename T 부분이 사용자가 입력한 자료형으로 바뀐다. 그리고 template 이하 코드를 복사 붙여넣기 한다. 그래서 int 자료형으로 한번 호출하고 float 자료형으로 또 한 번 호출하면 함수의 Overloading과 동일한 코드가 더 만들어진다.

 

 

Template를 활용하여 동적 할당하기

 해당 함수가 컴파일 타임에 복사, 붙여넣기되어 새로 생성된단는 점을 이용하면 C스타일의 [ ] list를 동적으로 만들어볼 수 있다. 아래 예제를 보자.

class A
{
public:
    void printSize()
    { 
        cout << "Size\t=" << Size << endl;
        cout << "Size\t=" << sizeof(Array)/sizeof(Array[0]) << endl; 
    }
private:
    int Size=4;
    int Array[Size];
};

int main()
{
    A a;
    a.printSize();
}

Array 리스트는 컴파일 타임에 Stack 형식으로 데이터가 쌓인다. 그래서 Size를 동적으로 할당해줄 수 없다. template N을 사용하면 함수가 호출될 때 template아래에 있는 코드를 새로 복사해서 붙여넣은 뒤에 N으로 작성된 변수만 사용자가 입력한 변수로 바꾸어놓는다. 따라서 아래 코드는 에러 없이 작동한다.

template<int N>
class A
{
public:
    void printSize()
    { 
        cout << "N\t=" << N << endl;
        cout << "Size\t=" << sizeof(Array)/sizeof(Array[0]) << endl; 
    }
private:
    int Array[N];
};

int main()
{
    A<5> a;
    a.printSize();
}

이 코드를 int 뿐 아니라 다양한 자료형에서도 활용할 수 있다. typename T를 추가해주고 아래 Array의 자료형을 변경해주자.

template<typename T, int N>
class A
{
public:
    void printSize()
    { 
        cout << "N\t=" << N << endl;
        cout << "Size\t=" << sizeof(Array)/sizeof(Array[0]) << endl; 
    }
private:
    T Array[N];
};

다양한 STL을 활용할 수 있는 Print함수 만들기

template<typename Container>
void printContainer(const Container &con)
{
    typename Container::const_iterator itr;

    for(itr = con.begin(); itr != con.end(); ++itr)
    {
        cout << *itr << " ";
    }
    cout << endl;
}

 

Comments