2장 템플릿

템플릿은 코드관리를 쉽게 해주기 때문에 자료구조에서 상당히 중요한 개념이다.


템플릿이란 무엇인가 ?

템플릿 함수를 만드는 방법

템플릿 클래스를 만드는 방법

복수의 템플릿 매개변수들을 사용하는 방법

값을 템플릿 매개변수로 사용하는 방법

템플릿의 한계와 문제점

템플릿의 내부적인 작동 방식


템플릿이란 무엇인가

코드를 여러가지 서로 다른 타입들에 대해 재사용할 수 있도록 해주는 것

뭔가를 반복해서 찍어내는 틀.

장점 : 어떤 타입에 대해 작성한 코드를 다른 자료형에 맞게 바꾸기 위해 복붙할 필요가 없다.


템플릿 함수

일반화된 자료형에 대해 작동하는 하나의 함수

같은 함수를 서로 다른 자료형들에 대해 재사용할 수 있도록 하는 수단


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int SumInterger(int* arr, int n)
{
    int sum = 0;
    for (int i = 0; i < n; ++i)
        sum += arr[i];
    return sum;
};
//이걸 float으로 바꾸려면 복붙해서 int 를 float으로 바꿔야 한다...
 
float SumFloats(float* arr, int n)
{
    float sum = 0;
    for (int i = 0; i < n; ++i)
        sum += arr[i];
    return sum;
}
cs

만약 함수의 내용을 조금 바꾸려면 이제 모든 함수를 바꿔야 한다.


템플릿으로 해결

동일한 함수를 서로 다른 자료형들에 대해 사용할 수 있으려면 템플릿을 쓰면 된다.!

1
template <typename T>
cs

이 다음에 나올 코드가 템플릿이다. 위에서 일반화generic된 자료형의 이름은 T(template)

T를 parameterized type 이라고도 한다.


1
2
3
4
5
6
7
8
template <typename T>
T Sum(T* arr, int n)
{
    T sum{};
    for (int i = 0; i < n; ++i)
        sum += arr[i];
    return sum;
}
cs

T 대신 DataType이나 SumType같은 서술적인 이름을 사용할 수도 있다.

기존 클래스와 충돌하지 않을 만한 이름을 쓰자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
    int intArr[10];
    float floatArr[10];
 
    for (int i = 1; i <= 10++i)
    {
        intArr[i - 1= i;
        floatArr[i - 1= 0.1f * i + i;
    }
 
    cout << SumInterger(intArr, 10<< endl;
    cout << SumFloats(floatArr, 10<< endl;
 
    cout << Sum(intArr, 10<< endl;
    cout << Sum(floatArr, 10<< endl;
}
cs


템플릿 클래스

일반화된 자료형에 대해 작동하는 하나의 완전한 클래스


기존의 방식


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class IntAdder
{
public:
    void Add(int n)
    {
        m_iSum += n;
    }
    int Sum()
    {
        return m_iSum;
    }
 
private:
    int m_iSum{};
};
 
 
class FloatAdder
{
public:
    void Add(float n)
    {
        m_fSum += n;
    }
    float Sum()
    {
        return m_fSum;
    }
 
private:
    float m_fSum{};
};
 
cs


각 자료형마다 만드는 대신, 템플릿 클래스를 만들면 된다!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
template <class T>
class Adder
{
public:
 
    void Add(const T& n) {
        m_sum += n;
    }
    T& Sum() {
        return m_sum;
    }
 
private:
    T m_sum{};
};
 
int main()
{
    IntAdder iAdder1;
    Adder<int> iAdder2;
    FloatAdder fAdder1;
    Adder<float> fAdder2;
    int i = 0;
    float f = 0.0f;
    for (; i < 10++i, f += 1.1f)
    {
        iAdder1.Add(i);
        iAdder2.Add(i);
        fAdder1.Add(f);
        fAdder2.Add(f);
    }
    cout << iAdder1.Sum() << endl;
    cout << iAdder2.Sum() << endl;
    cout << fAdder1.Sum() << endl;
    cout << fAdder2.Sum() << endl;
 
}
cs



두 개의 타입 인자 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template < class Sumtype, class DivideType >
DivideType GetAverage(Sumtype* arr, DivideType dt)
{
    Sumtype sum{};
    for (int i = 0; i < dt; ++i)
    {
        sum += arr[i];
    }
    return sum / dt;
}
 
int main()
{
    int arr[10= { 12345678910 };
    cout << GetAverage(arr, 10.f) << endl;
 
}
cs


 값을 템플릿 매개변수로 사용


 특정 자료형의 값(value)를 템플릿 매개변수로 사용할 수도 있다.

 일반화된 자료형의 값을 템플릿 매개변수로 사용할 수도 있다.


 특정 자료형의 값을 사용

 template< datatype value >

템플릿 매개변수의 값은 반드시 컴파일 시점에서 결정되어야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <class Datatype, int size>
class MyArray
{
public:
    void Set(Datatype val, int index)
    {
        m_arr[index] = val;
    }
    const Datatype& Get(int index)
    {
        return m_arr[index];
    }
 
    void clear(int index, Datatype zero)
    {
        m_arr[index] = zero;
    }
 
private:
    Datatype m_arr[size]{};
};
cs


서로 다른 값 매개변수를 가진 템플릿 클래스들은 완전히 다른 형식으로 간주된다.

템플릿으로 구현시 범위 체크를 쉽게 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
    MyArray<int10> iArr10;
 
    for (int i = 0; i < 10++i)
    {
        iArr10.Set(i, i);
        cout << iArr10.Get(i) << ' ';
    }
    cout << endl;
    iArr10.clear(52323);
 
    for (int i = 0; i < 10++i)
    {
        cout << iArr10.Get(i) << ' ';
    }
}
cs


템플릿의 문제점

매개변수화된 자료형의 특정 멤버 함수가 다른 자료형에도 있을 지 생각해봐야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template <class T>
void Function(T p_item)
{
    p_item.DoSomething();
}
 
class testA
{
public:
    void DoSomething() {};
};
 
class testB
{
public:
    void DoSomethingElse() {};
};
 
int main()
{
    testA a;
    testB b;
    Function(a);
    Function(b);
 
}
cs



심각도 코드 설명 프로젝트 파일 비표시 오류(Suppression) 상태

오류 C2039 'DoSomething': 'testB'의 멤버가 아닙니다. ConsoleApplication1 c:\users\administrator\desktop\consoleapplication1\consoleapplication1\소스.cpp 4



템플릿의 내부적인 동작 방식

C++은 템플릿 정의를 복사했다가 실제 쓰이는 부분에 붙여놓고

매개변수화된 형식을 실제 자료형으로 치환한다.

즉 우리가 복붙 할 걸 컴파일러가 대신 복붙한다는 소리!

#define 매크로와 비슷하지만 이는 전처리기에 의해 처리되는 반면

템플릿은 컴파일러에 의해 처리되어 타입안전성이 보장된다.



결론

템플릿의 구문이 조금 복잡하고 난해한 면이 있지만 그것은 템플릿의 유용함에 비하면 아주 사소한 단점일 뿐이다.

'프로그래밍 책 공부' 카테고리의 다른 글

23장 길찾기  (0) 2018.04.02
게임 프로그래머를 위한 자료구조와 알고리즘 - 목차  (0) 2018.03.23
1. 디자인 패턴 소개  (0) 2018.03.19
목차와 서문  (0) 2018.03.19