본문 바로가기

Programming/C++

c++ 템플릿 공부


두 값을 더하는 add연산을 하는 함수를 만들어야 한다는 상황을 가정해보자

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
#include <iostream>
 
/*
int add(int a, int b) {
    return a + b;
}
double add(double a, double b) {
    return a + b;
}
std::string add(std::string a, std::string b) {
    return a + b;
}
*/
 
template <typename T>
T add(T a, T b) {
    return a + b;
}
 
// 특수화 (Specialization)
template <typename T>
int add(int* a, int* b) {
    return *+ *b;
}
 
int main(void) {
    int a = 10, b = 20;
    double c = 10.4, d = 20.4;
    std::string s = "Hello", t = "World";
    int* pA = &a;
    int* pB = &b;
 
    int sum = add(a, b);
    double sum2 = add(c, d);
    std::string sum3 = add(s, t);
    int pSum = add<int*>(pA, pB);    // using specialization template
 
    std::cout << pSum << std::endl;
}
cs

템플릿이 없었다면 주석처럼 각 타입마다 모두 add함수를 만들어서 사용해야 할 것이다.
그런데 템플릿을 사용하면 이렇게 타입에 대해서 T로 표시하고 어찌어찌 해서 들어오는 타입대로 더할 수 있게 된다.
특정 자료형만 따로 정의할 일이 있으면 위에처럼 특수화를 해서 정의하면 된다.

그럼 int와 double은 어떻게 더해야 하는가? 
int + double은 double이지만 어떤 값이 double인지 알아내기는 쉽지 않다.
여러가지 대처 방법이 있을 수 있는데 가장 무난하고 권장되는 방법은

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
 
template <typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
    return a + b;
}
 
int main(void) {
    auto e = 10.4f;
    decltype(e) f = 24.5f;    // e의 타입으로 정의
 
    int a = 10;
    double b = 20.4;
 
    std::cout << add(a, b) << std::endl;
    std::cout << add(b, a) << std::endl;
}
cs

이렇게 auto로 반환하되 auto의 타입을 decltype을 이용해서 a + b의 타입으로 맞춰주면 된다.

다음은 가변인자에 대한 가변 템플릿이다.

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
#include <iostream>
 
/*
int sum(int a, int b) {
    return a + b;
}
int sum(int a, int b, int c) {
    return a + b + c;
}
int sum(int a, int b, int c, int d) {
    return a + b + c + d;
}
int sum(int a, int b, int c, int d, int e) {
    return a + b + c + d + e;
}
// ...
*/
 
// C++11 : Variadic Template (가변 템플릿)
template <typename T, typename... Args>
T sum(T a, Args... args) {
    return a + sum(args...);
}
 
template <typename T>
T sum(T a) {
    return a;
}
 
int main(void) {
    int a = 1, b = 2, c = 3;
 
    std::cout << sum(a, b) << std::endl;
    std::cout << sum(a, b, c) << std::endl;
}
cs

가변 템플릿이 없었다면 주석에 있는 것처럼 모든 인자의 개수에 맞춰서 다 정의를 미리 해주어야한다.
그런데 가변 템플릿을 사용하면 그럴 필요가 없다.

가변 템플릿은 재귀적인 형태로 이루어지며 가변 인수를 받는 템플릿 함수와 재귀적으로 돌다가 인자가 하나만 남았을 때 처리하는 함수 총 두개가 필요하다.

가변 템플릿은 위에 처럼 쓰면 댄다.
그리고 저 가변 템플릿은 코드상에서는 재귀지만 컴파일 타임때 재귀는 모두 풀리고 그냥 함수로 풀려서 실제 컴파일 하면 재귀로 실행되지 않는다.
그래서 재귀함수의 오버헤드가 발생하지 않는다.

그럼 가변 템플릿과 이것 저것을 응용해서 

1
2
3
4
5
6
#include <iostream>
 
int main(void) {
    std::cout << 'a' << 3 << "String" << 3.14 << std::endl;
    PrintList('a'3"String"3.14);
}
cs

4번 라인과 5번 라인이 똑같이 나오도록 PrintList함수를 만들어보자.

물론 인자 개수와 타입은 각각 바뀔 수 있다.

알아서 만들자

'Programming > C++' 카테고리의 다른 글

C++에서 문자열 EOF까지 입력받고 출력하기  (0) 2018.04.24
Lvalue와 Rvalue  (0) 2017.05.20