도순씨의 코딩일지

C++ :: 템플릿의 특수화, 인자, 매개변수, 디폴트값 본문

𝐏𝐑𝐎𝐆𝐑𝐀𝐌𝐌𝐈𝐍𝐆/𝐂++

C++ :: 템플릿의 특수화, 인자, 매개변수, 디폴트값

도순씨 2020. 9. 1. 00:00

🌼 Point 클래스 템플릿과 배열 클래스 템플릿

다음과 같은 형태는 생소하지만 템플릿 클래스의 객체를 저장할 수 있습니다.

1
BoundCheckArray<Point<int>> oarr(50);
cs

 

만약 저장대상이 Point<int> 템플릿 클래스 객체가 아닌 Point<int>형 포인터라면 다음과 같이 표현 가능합니다.

1
BoundCheckArray<Point<int>*> oparr(50);
cs

 

예제를 통해서 직접 살펴봅시다.

 

⭐️ PointTemplate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef __POINT_TEMPLATE_H_
#define __POINT_TEMPLATE_H_
 
template <typename T>
class Point{
private:
    T xpos, ypos;
public:
    Point(T x = 0 , T y = 0);
    void ShowPosition() const;
};
 
template <typename T>
Point<T> :: Point(T x, T y) : xpos(x), ypos(y) {}
 
template <typename T>
void Point<T>::ShowPosition() const {
    cout << '[' << xpos << ", " << ypos << ']' << endl;
}
 
#endif
 
cs

 

⭐️ ArrayTemplate.h

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#ifndef __ARRAY_TEMPLATE_H_
#define __ARRAY_TEMPLATE_H_
 
#include <iostream>
#include <cstdlib>
using namespace std;
 
template <typename T>
class BoundCheckArray{
private:
    T* arr;
    int arrlen;
    BoundCheckArray(const BoundCheckArray& arr){}
    BoundCheckArray& operator=(const BoundCheckArray& arr){}
public:
    BoundCheckArray(int len);
    T& operator[](int idx);
    T operator[](int idx) const;
    int GetArrLen() const;
    ~BoundCheckArray();
};
 
template <typename T>
BoundCheckArray<T> :: BoundCheckArray(int len) : arrlen(len) {
    arr = new T[len];
}
 
template <typename T>
T& BoundCheckArray<T> :: operator[] (int idx){
    if(idx < 0 || idx >= arrlen){
        cout << "Array index out of bound exception" << endl;
        exit(1);
    }
    return arr[idx];
}
 
template <typename T>
T BoundCheckArray<T> :: operator[] (int idx) const{
    if(idx < 0  || idx >= arrlen){
        cout << "Array index out of bound exception" << endl;
        exit(1);
    }
    return arr[idx];
}
 
template <typename T>
int BoundCheckArray<T> :: GetArrLen() const {
    return arrlen;
}
 
template <typename T>
BoundCheckArray<T> :: ~BoundCheckArray() {
    delete []arr;
}
 
#endif
cs

 

⭐️ BoundArrayMain.cpp

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
#include <iostream>
#include "ArrayTemplate.h"
#include "PointTemplate.h"
using namespace std;
 
int main(void){
    BoundCheckArray<Point<int>> oarr1(3);
    oarr1[0= Point<int>(34);
    oarr1[1= Point<int>(56);
    oarr1[2= Point<int>(78);
 
    for(int i = 0 ; i < oarr1.GetArrLen() ; i++)
        oarr1[i].ShowPosition();
 
    BoundCheckArray<Point<double>> oarr2(3);
    oarr2[0= Point<double>(3.144.31);
    oarr2[1= Point<double>(5.096.07);
    oarr2[2= Point<double>(7.828.54);
 
    for(int i = 0 ; i < oarr2.GetArrLen() ; i++)
        oarr2[i].ShowPosition();
 
    typedef Point<int>* POINT_PTR;
    BoundCheckArray<POINT_PTR> oparr(3);
    oparr[0= new Point<int>(1112);
    oparr[1= new Point<int>(1314);
    oparr[2= new Point<int>(1516);
 
    for(int i = 0 ; i < oparr.GetArrLen() ; i++)
        oparr[i] -> ShowPosition();
 
    delete oparr[0]; delete oparr[1]; delete oparr[2];
    return 0;
}
cs

 

⭐️ 실행결과

1
2
3
4
5
6
7
8
9
[3, 4]
[5, 6]
[7, 8]
[3.14, 4.31]
[5.09, 6.07]
[7.82, 8.54]
[11, 12]
[13, 14]
[15, 16]
cs

 

🌼 클래스 템플릿의 특수화

클래스 템플릿을 특수화하는 이유는 특정 자료형을 기반으로 생성된 객체에 대해, 구분이 되는 다른 행동양식을 적용하기 위해서입니다.

 

⭐️ ClassTemplateSpecialization.cpp

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cstring>
using namespace std;
 
template <typename T>
class Point{
private:
    T xpos, ypos;
public:
    Point(T x = 0, T y = 0) : xpos(x), ypos(y){}
    void ShowPosition() const{
        cout << '[' << xpos << ", " << ypos << ']' << endl;
    }
};
 
template <typename T>
class SimpleDataWrapper{
private:
    T mdata;
public:
    SimpleDataWrapper(T data) : mdata(data){}
    void ShowDataInfo(void){
        cout << "Data: " << mdata << endl;
    }
};
 
template<>
class SimpleDataWrapper<char *>{
private:
    char* mdata;
public:
    SimpleDataWrapper(char* data){
        mdata = new char[strlen(data) + 1];
        strcpy(mdata, data);
    }
    void ShowDataInfo(void){
        cout << "String: " << mdata << endl;
        cout << "Length: " << strlen(mdata) << endl;
    }
    ~SimpleDataWrapper() {delete []mdata;}
};
 
template <>
class SimpleDataWrapper<Point<int>>{
private:
    Point<int> mdata;
public:
    SimpleDataWrapper(int x, int y) : mdata(x, y){}
    void ShowDataInfo(void){
        mdata.ShowPosition();
    }
};
 
int main(void){
    SimpleDataWrapper<int> iwrap(170);
    iwrap.ShowDataInfo();
    SimpleDataWrapper<char*> swrap("Class Template Specialization");
    swrap.ShowDataInfo();
    SimpleDataWrapper<Point<int>> poswrap(37);
    poswrap.ShowDataInfo();
    return 0;
}
cs

 

⭐️ ClassTemplateSpecialization.cpp 실행결과

1
2
3
4
Data: 170
String: Class Template Specialization
Length: 29
[3, 7]
cs

 

🌼 클래스 템플릿의 부분 특수화

아래와 같은 클래스가 있다고 가정해봅시다.

1
2
template <typename T1, typename T2>
class MySimple{....}
cs

 

만약 T1과 T2를 각각 char과 int로 하여 특수화를 진행한다고 가정해봅시다. 다음과 같이 형태를 바꿀 수 있습니다.

1
2
template <>
class MySimple<char, int> {....}
cs

 

예제를 통해 다시 확인해봅시다.

 

⭐️ ClassTemplatePartialSpecialization.cpp

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
38
39
40
41
42
43
44
#include <iostream>
using namespace std;
 
template <typename T1, typename T2>
class MySimple{
public:
    void WhoAreyou(){
        cout << "size of T1: " << sizeof(T1) << endl;
        cout << "size of T2: " << sizeof(T2) << endl;
        cout << "<typename T1, typename T2>" << endl;
    }
};
 
template<>
class MySimple<intdouble>{
public:
    void WhoYouAre(){
        cout << "size of int: " << sizeof(int<< endl;
        cout << "size of double: " << sizeof(double<< endl;
        cout << "<int, double>" << endl;
    }
};
 
/*
template <typename T1>
class MySimple<T1, double>{
public:
     void WhoAreayou(){
         cout << "size of T1: " << sizeof(T1) << endl;
         cout << "size of double" << sizeof(double) << endl;
         cout << "<T1, double>" << endl;
     }
 };
 */
 
int main(void){
    MySimple<chardouble> obj1;
    obj1.WhoAreyou();
    MySimple<intlong> obj2;
    obj2.WhoAreyou();
    MySimple<intdouble> obj3;
    obj3.WhoYouAre();
    return 0;
}
cs

 

⭐️ ClassTemplatePartialSpecialization.cpp 실행결과

1
2
3
4
5
6
7
8
9
size of T1: 1
size of T2: 8
<typename T1, typename T2>
size of T1: 4
size of T2: 8
<typename T1, typename T2>
size of int: 4
size of double: 8
<int, double>
cs

 

위 주석처리된 상태에서는 <int, double>에 대해서만 특수화 처리가 되어 있습니다. 주석을 해제하면 어떻게 될까요? <T1, double>에 대해서 부분 특수화가 진행됩니다.

 

⭐️ ClassTemplatePartialSpecialization.cpp 실행결과

1
2
3
4
5
6
7
8
9
size of T1: 1
size of double8
<T1, double>
size of T1: 4
size of T2: 8
<typename T1, typename T2>
size of int: 4
size of double: 8
<int, double>
cs

 

🌼 템플릿 인자

결정되지 않은 자료형을 의미하는 용도로 사용되는 T와 같은 문자를 '템플릿 매개변수'라고 합니다. 그리고 템플릿 매개변수에 전달되는 자료형 정보를 '템플릿 인자'라고 합니다.

 

다음 예제를 살펴봅시다.

1
2
3
4
5
6
7
8
9
template <typename T, int len>
class SimpleArray{
    private:
        T arr[len];
    public:
        T& operator[] (int idx){
            return arr[idx]
        }
};
cs

이렇게 템플릿 매개변수에도 변수가 올 수 있습니다. 예제를 한 번 봅시다.

 

⭐️ NonTypeTemplateParam.cpp

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
38
#include <iostream>
using namespace std;
 
template <typename T, int len>
class SimpleArray{
private:
    T arr[len];
public:
    T& operator[] (int idx) { return arr[idx];}
    SimpleArray<T, len>& operator=(const SimpleArray<T, len>& ref){
        for(int i = 0 ; i < len ; i++)
            arr[i] = ref.arr[i];
        return *this;
    }
};
 
int main(void){
    SimpleArray<int5> i5arr1;
    for(int i = 0 ; i < 5 ; i++)
        i5arr1[i] = i * 10;
 
    SimpleArray<int5> i5arr2;
    i5arr2 = i5arr1;
    for(int i = 0 ; i < 5 ; i++)
        cout << i5arr2[i] << ", ";
    cout << endl;
 
    SimpleArray<int7> i7arr1;
    for(int i = 0 ; i < 7 ; i++)
        i7arr1[i] = i * 10;
 
    SimpleArray<int7> i7arr2;
    i7arr2 = i7arr1;
    for(int i = 0 ; i < 7 ; i++)
        cout << i7arr2[i] << ", ";
    cout << endl;
    return 0;
}
cs

 

⭐️ NonTypeTemplateParam.cpp 실행결과

1
2
0, 10, 20, 30, 40, 
0, 10, 20, 30, 40, 50, 60, 
cs

 

길이가 같은 두 배열 객체간의 대입만이 허용됩니다. 이렇듯 템플릿 매개변수에 값을 저장받을 수 있는 변수를 선언하면, 변수에 전달되는 상수를 통해서 서로 다른 형의 클래스가 생성되게 할 수 있습니다.

 

🌼 템플릿 매개변수와 디폴트 값

템플릿 매개변수에도 디폴트 값 지정이 가능합니다. 다음 예제를 살펴봅시다.

 

⭐️ TemplateParamDefaultValue.cpp

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
#include <iostream>
using namespace std;
 
template <typename T = intint len = 7>       // 디폴트 값 지정
class SimpleArray{
private:
    T arr[len];
public:
    T& operator[] (int idx){ return arr[idx];}
    SimpleArray<T, len>& operator= (const SimpleArray<T, len> &ref){
        for(int i = 0 ; i < len ; i++)
            arr[i] = ref.arr[i];
        return *this;
    }
};
 
int main(void){
    SimpleArray<> arr;
    for(int i = 0 ; i < 7 ; i++)
        arr[i] = i + 1;
    for(int i = 0 ; i < 7 ; i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
cs

 

⭐️ TemplateParamDefaultValue.cpp 실행결과

1
1 2 3 4 5 6 7 
cs

 

 

Comments