티스토리 뷰
템플릿 - 함수 템플릿
1. 함수템플릿 - 여러 함수를 만들어 내는 틀
2. 클래스 템플릿 - 여러 클래스를 만들어 내는 틀, 참고 : http://petra.tistory.com/1225
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
#include "stdafx.h"
#include <iostream>
using namespace std;
void Print(int a, int b) {
cout << a << ", " << b << endl;
}
void Print(double a, double b) {
cout << a << ", " << b << endl;
}
void Print(const char* a, const char* b) { // const 없으면 컴파일 에러
cout << a << ", " << b << endl;
}
int main()
{
Print(10, 20);
Print(0.123, 1.123);
Print("ABC", "abcd");
_gettch();
return 0;
} |
cs |
함수 오버로딩을 이요하여 구현해서 각 매개변수 타입을 각각 정의하고 있다.
위 코드는 이미 타입을 알고 있는것에 한해서만 가능하다는 문제가 있는데, 템플릿 함수를 사용하면 템플릿 매개변수 타입을 보고 타입을 결정하여 실제 함수인 템플릿 인스턴스 함수를 만들어 낸다.
함수 템플릿 이름 앞에 template<typename T> 키워드만 붙이고, 함수 매개변수 타입을 T로 지정한다.
void Print(int a, int b) 를
template<typename T> void Print(T a, T b) 로 바꾸면 된다.
(참고로, template <class T> 는 표준화 이전부터 사용해온것이라 대부분의 컴파일러는 둘다 지원하고 있다)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
#include "stdafx.h"
#include <iostream>
using namespace std;
template<typename T> void Print(T a, T b) {
cout << a << ", " << b << endl;
}
int main()
{
Print(10, 20); // Print<int>(10, 20) 와 같음.
Print(0.123, 1.123); // Print<double>(0.123, 1.123)
Print("ABC", "abcd"); // Print<const char*>("ABC", "abcd")
_gettch();
return 0;
} |
cs |
<실행>
위 코드는 template<typename T> void Print<T>()으로 실제 함수 3개를 만든다.
세 함수는 각각 void Print<int>(), void Print<double>(), void Print<constr char*>() 가 된다.
이 세 함수를 tempalte<typename T>void Print<T>() 템플릿의 인스턴스라고 한다.
함수 템플릿 인스턴스는 컴파일러가 생성하는 함수 정의 코드이다.
5~7행은 결국 컴파일러에 의해 아래와 같은 3개의 함수 인스턴스 코드를 만든다.
1
2
3
4
5
6
7
8
9 |
void Print<int>(int a, int b) {
cout << a << ", " << b << endl;
}
void Print<double>(double a, double b) {
cout << a << ", " << b << endl;
}
void Print<const char*>(const char* a, const char* b) {
cout << a << ", " << b << endl;
} |
cs |
템플릿도 함수 처럼 여러 매개변수를 가질 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
#include "stdafx.h"
#include <iostream>
using namespace std;
template<typename T1, typename T2> void Print(T1 a, T2 b) {
cout << a << ", " << b << endl;
}
int main()
{
Print(10, 1.5); // Print<int, double>(10, 1.5) 와 같음
Print("Hello", 10); // Print<const char*, int>("Hello", 10);
Print(1.5, "Hello"); // Print <double, const char*>(1.5, "Hello");
_gettch();
return 0;
} |
cs |
<실행>
템플릿의 매개변수는
템플릿 함수 정의의 연산이 가능한 객체, 즉, 인터페이스를 지원하는 객체라면 모두 올 수 있다.
다시 말해서, T temp = a 문장에서 복사 생성자를 호출하므로 복사 생성자(인터페이스)를 지원해야 하며
a= b 나 b = temp 문장에서 대입 연산자를 호출하므로 대인 연산자(인터페이스)를 지원해야 한다.
(배열 출력하는 템플릿)
14행에서 암시적호출은 불가능한것이
PrintArray(arr1) 이라고 호출하면, 배열 요소 갯수가 5개란것을 알 수 없기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 |
#include "stdafx.h"
#include <iostream>
using namespace std;
template<typename T, int size>
void PrintArray(T* arr) {
for (int i = 0; i < size; i++)
cout << "[" << i << "] : " << arr[i] << endl;
cout << endl;
}
int main()
{
int arr[5] = { 10, 20, 30, 40, 50 };
PrintArray<int, 5>(arr); // 명시적 호출
double arr1[3] = { 1.1, 2.2, 3.3 };
PrintArray<double, 3>(arr1); // 명시적 호출
_gettch();
return 0;
} |
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 |
#include "stdafx.h"
#include <iostream>
using namespace std;
class Point {
int x, y;
public:
explicit Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
void Print() { cout << x << ", " << y << endl; }
};
template<typename T> void Print(T a) {
cout << a << endl;
}
int main()
{
int n = 10;
double d = 2.5;
Point pt(2, 3);
Print(n);
Print(d);
Print(pt); // 컴파일 에러
_gettch(); return 0;
} |
cs |
위 코드를 컴파일하면 12행에서
C2679 이항 '<<': 오른쪽 피연산자로 'Point' 형식을 사용하는 연산자가 없거나 허용되는 변환이 없습니다.
라는 에러가 발생한다.
cout << pt 연산을 할 수 없어 Point 객체를 화면 출력할 수 없기 때문이다.
해결방법>
1) Point 클래스에 << 연산자 오버로딩 함수를 추가한다.
하지만, 소스 코드로 지원하지 않는 라이브러리 인 경우 클래스 수정이 불가능할 경우 이 방법으로 안된다.
2) Point 객체만의 특수환된 함수 템플릿을 만든다.
두번째 방법으로 수정한 코드
위 코드에서 14~16행을 추가한다.
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 |
#include "stdafx.h"
#include <iostream>
using namespace std;
class Point {
int x, y;
public:
explicit Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
void Print() { cout << x << ", " << y << endl; }
};
template<typename T> void Print(T a) {
cout << a << endl;
}
template<> void Print(Point a) { // << 연산을 못하는 Point 용 특수화 함수 템플릿
a.Print();
}
int main()
{
int n = 10;
double d = 2.5;
Point pt(2, 3);
Print(n);
Print(d);
Print(pt); // Print<>(pt) 와 같음
_gettch();
return 0;
} |
cs |
<실행>
(STL for-ecah 반복자 구현)
배열의 처음부터 마지막까지 인자로 받아 모든 원소에 대해 함수를 호출하여 원소값을 출력한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
#include "stdafx.h"
#include <iostream>
using namespace std;
void For_each(int* begin, int *end, void(*pf)(int)) {
while (begin != end) pf(*begin++);
}
void PrintInt(int n) { cout << n << " "; }
int main()
{
int arr[5] = { 10, 20, 30, 40, 50 };
For_each(arr, arr + 5, PrintInt);
cout << endl;
_gettch();
return 0;
} |
cs |
<실행>
위 코드는 정수형만 사용 가능한데 원소 타입에 상관없이 사용할 수 있는 일반적인 함수로 만들어 활용도를 높이고 유지 보수를 좋게 바꿔 본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 |
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template<typename IterT, typename Func>
void For_each(IterT begin, IterT end, Func pf) {
while (begin != end) pf(*begin++);
}
void PrintInt(int data) { cout << data << " "; }
void PrintString(string data) { cout << data << " "; }
int main()
{
int arr[5] = { 10, 20, 30, 40, 50 };
For_each(arr, arr + 5, PrintInt); // 암시적 호출, 정수 출력
//For_each<int*, void(*)(int)>(arr, arr + 5, PrintInt); // 명시적 호출
cout << endl;
string sarr[3] = { "abc", "ABC", "Hello" };
For_each(sarr, sarr + 3, PrintString); // 문자열 출력
_gettch();
return 0;
} |
cs |
<실행>
컴파일러는 For_each<int*, void(*)(int)>() 와 For_each<string*, void(*)(string)>()를 코드를 만든다.
Printint(), PrintString() 도 템플릿 함수로 만들어 하나로 만들 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template<typename IterT, typename Func>
void For_each(IterT begin, IterT end, Func pf) {
while (begin != end) pf(*begin++);
}
template<typename T>
void Print(T data) { cout << data << " "; }
int main()
{
int arr[5] = { 10, 20, 30, 40, 50 };
For_each(arr, arr + 5, Print<int>); // Print<int> 명시적 매개변수로 전달해야 한다
cout << endl;
string sarr[3] = { "abc", "ABC", "Hello" };
For_each(sarr, sarr + 3, Print<string>); // 문자열 출력
_gettch();
return 0;
} |
cs |
<실행>
Print()를 함수 객체로 바꿔 부가적인 서비스를 객체가 제공할 수 있게 한다.
함수 객체의 멤버 변수를 만들어 정수일때와 문자열일때 구분자를 두어 부가 정보를 출력 할 수 있다.
숫자일때는 공백으로, 문자열일때는 문자열 끝에 "*\n" 을 붙여주는 부가 기능을 추가할 수 있다.
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 |
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template<typename IterT, typename Func>
void For_each(IterT begin, IterT end, Func pf) {
while (begin != end) pf(*begin++);
}
template<typename T>
struct PrintFunctor {
string sep; // 출력 구분자 정보
public:
explicit PrintFunctor(const string& s = " ") : sep(s) {}
void operator()(T data) const {
cout << data << sep;
}
};
int main()
{
int arr[5] = { 10, 20, 30, 40, 50 };
For_each(arr, arr + 5, PrintFunctor<int>()); // 암시적 호출, 정수 출력
cout << endl;
string sarr[3] = { "abc", "ABC", "Hello" };
For_each(sarr, sarr + 3, PrintFunctor<string>("*\n")); // 문자열 출력
_gettch();
return 0;
} |
cs |
<실행>
(STL 의 less 와 greater 를 템플릿 객체로 구현)
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 |
#include "stdafx.h"
#include <iostream>
#include <functional>
using namespace std;
template<typename T>
struct Less {
bool operator()(T a, T b) {
return a < b;
}
};
template<typename T>
struct Greater {
bool operator()(T a, T b) {
return a > b;
}
};
int main()
{
cout << Less<int>()(10, 20) << endl; // 사용자 Less, Greater 사용
cout << Less<int>()(20, 10) << endl;
cout << Less<double>()(2.2, 1.1) << endl;
cout << Greater<int>()(10, 20) << endl;
cout << Greater<int>()(20, 10) << endl;
cout << endl;
cout << less<int>()(10, 20) << endl; // STL less, greater 사용
cout << less<int>()(20, 10) << endl;
cout << greater<int>()(10, 20) << endl;
cout << greater<int>()(20, 10) << endl;
_gettch();
return 0;
} |
cs |
<실행>
(팀플릿 매개변수와 함수 객체를 결합하면 반환 타입과 함수 매개변수 타입을 클라이언트가 결정하는 유연한 함수 객체를 만들어 본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 |
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template<typename RetType, typename ArgType>
class Functor {
public:
RetType operator()(ArgType data) {
cout << data << endl;
return RetType(); // 자료형의 기본생성자 호출된 값(초기화)을 반환하는듯함.
}
};
int main()
{
Functor<void, int> functor1;
functor1(10);
Functor<bool, string> functor2;
functor2("Hello");
_gettch();
return 0;
} |
cs |
<실행>
(STL pair 구현)
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 "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template<typename T1, typename T2>
struct Pair {
T1 first; T2 second;
public:
Pair(const T1& ft, const T2& sd) : first(ft), second(sd) {}
};
int main()
{
Pair<int, int> p1(10, 20);
cout << p1.first << ", " << p1.second << endl;
Pair<int, string> p2(1, "one");
cout << p2.first << ", " << p2.second << endl;
cout << endl;
pair<int, int> p3(10, 20); // STL pair 사용
cout << p3.first << ", " << p3.second << endl;
pair<int, string> p4(1, "one");
cout << p4.first << ", " << p4.second << endl;
_gettch();
return 0;
} |
cs |
<실행>