가변 길이 템플릿
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>
template <typename T>
void print(T arg) {
std::cout << arg << std::endl;
}
template <typename T, typename...Types>
void print(T arg, Types... args) {
std::cout << arg << ", ";
print(args...);
}
int main() {
print(1, 3.1, "abc");
print(1, 2, 3, 4, 5, 6, 7);
}
|
cs |
typename <typename T, typename... Types>
-> 템플릿 파라미터 팩, 0개 이상의 템플릿 인자들을 나타낸다. (타입 앞에 ...)
void print(T arg, Types... args) {
-> 함수 파라미터 팩, 0개 이상의 함수 인자를 나타낸다. (타입 뒤에 ...)
파라미터 팩은 추론된 인자를 제외한 나머지 인자들을 나타낸다.
예를 들어 위의 코드 중 print(1, 3.1, "abc")를 호출할 경우 컴파일러는 두 개의 print() 중
어느것을 선택할지 정해야 한다. 첫 번쨰 print()는 1개의 인자만을 받으므로 제외되고 두 번째 print()가
선택된다.
만약 인자의 수가 같거나 0개일 경우 파라미터 팩이 없는 함수가 우선순위를 가진다.
c++컴파일러는 함수 컴파일 시 자신의 앞에 정의되어 있는 함수들 밖에 보지 못하므로 특정 함수의 존재 자체를
모를 수도 있다. 따라서 템플릿 함수 작성 시 순서에 유의해야 한다.
가변 길이 템플릿 예제 - 임의의 개수의 문자열을 합치는 함수
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
63
64
65
66
67
68
69
70
|
#include <iostream>
#include <cstring>
#include <string>
// 임의의 개수의 문자열을 받아서 각각의 길이를 더한 것들을 리턴
// const char* 으로 받는 경우
size_t GetStringSize(const char* s) {
return strlen(s);
}
// std::string 으로 받는 경우
size_t GetStringSize(const std::string& s) {
return s.size();
}
template <typename String, typename... Strings>
size_t GetStringSize(const String& s, Strings...strs) {
return GetStringSize(s) + GetStringSize(strs...);
}
// 아무 인자도 없을때까지 반복하므로 재귀호출을 끝내기 위해 아무 값도 전달해주지 않음
void AppendToString(std::string* concat_str) {
return;
}
// 첫 번째 인자로는 합쳐진 문자열을 보관할 문자열을 계속 전달
// 그 다음 인자들로는 합쳐질 문자열들을 계속 전달
template <typename String, typename...Strings>
void AppendToString(std::string* concat_str, const String& s, Strings... strs) {
concat_str->append(s);
AppendToString(concat_str, strs...);
}
template <typename String, typename... Strings>
std::string StrCat(const String& s, Strings...strs) {
// 합쳐질 문자열의 총 길이를 구한다
size_t total_size = GetStringSize(s, strs...);
// 합쳐진 문자열을 보관하는 변수
std::string concat_str;
// reserve() -> 메모리 저장할 공간을 예약
// 메모리가 필요없이 재할당 되는 것을 막아줌
// 어느정도의 메모리를 할당해 둠으로써 재할당 횟수를 최소화 시켜줌
// reserve()를 이용해 필요한 만큼 미리 공간을 할당
concat_str.reserve(total_size);
// 템플릿으로 받은 문자열을 concat_str 변수에 저장
concat_str = s;
// 템플릿으로 받은 나머지 인자들을 AppendToString()을 통해
// 붙여줌
AppendToString(&concat_str, strs...);
return concat_str;
}
int main() {
std::cout << StrCat(std::string("this"), " ", "is", " ", std::string("a"),
" ", std::string("sentence")) << std::endl;
}
|
cs |
sizeof
- 템플릿 전체 인자의 개수를 리턴
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
|
#include<iostream>
int sum_all() {
return 0;
}
// 전달된 인자들의 합을 리턴하는 함수
template <typename...Ints>
int sum_all(int num, Ints... nums) {
return num + sum_all(nums...);
}
// 전달된 전체 인자 개수로 합을 나눠주는 함수
template <typename...Ints>
double average(Ints...nums) {
// static_cast -> 형변환 (int형 -> double형 형변환)
// sizeof...() -> sizeof... 에 파라미터 팩을 전달하면
// nums에 해당하는 실제 인자의 개수를 리턴해준다.
return static_cast<double>(sum_all(nums...)) / sizeof...(nums);
}
int main() {
std::cout << average(1, 4, 2, 3, 10) << std::endl;
}
|
cs |
sizeof...에 파라미터 팩 (nums)를 전달해주면 nums에 해당하는 실제 인자의 개수를 리턴해준다.
Fold Expression
- 위의 예제 코드들 처럼 가변 길이 템플릿은 재귀 함수 형태로 구성해야 하기 때문에 반드시 재귀 호출 종료를
위한 함수를 따로 만들어야 했다. 하지만 Fold형식을 사용하면 이를 훨씬 간단하게 표현할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
template <typename...Ints>
int sum_all(Ints... nums) {
return (... + nums);
}
int main() {
std::cout << sum_all(1, 4, 2, 3, 10) << std::endl;
}
|
cs |
위의 예제 코드 중 return (... + nums); 부분이 Fold 형식으로 이 코드는 컴파일러에서
return ((((1+4) + 2) + 3) + 10); 과 같이 해석된다. 이와 같은 형태를 단항 좌측(Unary left fold) 이라고 한다.
'c++' 카테고리의 다른 글
클래스 (Class)와 구조체 (Struct)의 차이 (0) | 2020.08.28 |
---|---|
함수 템플릿 (0) | 2020.08.27 |
템플릿 (Template) (0) | 2020.08.27 |
오버라이딩, 가상함수 (0) | 2020.08.20 |
String class (0) | 2020.08.18 |