※ 템플릿 함수와 함수 템플릿
- C++에서 template은 다른 객체지향언어에서 부르는 "일반화를 뜻하는" 제네릭(generic)이다.
- template 사용은 약간의 비용(실행비용)이 발생하지만 전체적으로 프로그램의 크기와 난이도를 줄일 수 있다
예를들어 int, double, char 타입에 대한 최솟값을 구하는 min 함수를 만들어 보자면 아래와 같이 3개의 함수가 필요하다.
int min (int a, int b) { return a < b ? a : b; }
double min (double a, double b) { return a < b ? a : b; }
char min (char a, char b) { return a < b ? a : b; }
하지만 template을 사용한다면 모든 data type에 대한 정의가 하나의 함수로 가능하다. 아래처럼 말이다.
template<typename T>
T min (T a, T b) { return a < b ? a : b; }
cf. 위의 T는 템플릿 매개변수라 부른다.
※ 템플릿 함수와 함수 템플릿
※ 템플릿 함수
- 템플릿을 기반으로 컴파일러가 만들어 내는 함수(템플릿 기반의 함수임을 표기한 것)
- 즉, 템플릿 기반의 호출이 가능한 “함수”라는 점에서 차이가 있다.
※ 함수 템플릿
- 함수 템플릿은 함수를 만들어 낸다.(기능은 결정되어 있으나 자료형은 결정 X)
- 따라서 함수 템플릿으로 다양한 자료형의 함수를 만들 수 있다.
- 함수를 만드는데 사용되는 템플릿으로 호출이 가능한 함수가 아닌 “템플릿”이다
※ 템플릿의 종류
- 함수 템플릿: 함수의 오버로딩을 확장한 개념
template<typename T>
T min (T a, T b) { return a < b ? a : b; }
- 클래스 템플릿:클래스를 만드는 템플릿으로 내부의 멤버변수.함수의 data type 지정시 사용
- 템플릿 클래스: 클래스 템플릿을 기반으로 컴파일러가 만든 클래스이다.
다만, 클래스 템플릿 기반 객체생성에는 반드시 <int>와 같은 자료형을 명시해 줘야 한다!
template <typename T>
class Animal{
private:
T num;
public:
Animal(T num) : num(num) {}
T eat(const T& ref);
};
template <typename T>
T Animal<T>::eat(const T& ref) {}
int main() {
Animal<int> a(4);
a.eat(4);
}
- 타입 템플릿: using으로 data type을 템플릿으로 지정시 사용
template <typename T>
using ptr = T*;
ptr<int> x; // int *x와 동일한 선언
- 변수 템플릿: 변수에 적용할 수 있는 템플릿 (C++14 이후부터 적용)
template <typename T>
constexpr T PI = T(3.141592653589793238462643L);
※ 템플릿의 특수화
- 특정 자료형으로 생성된 객체에 대해 구분이 되는 다른 행동양식 적용하기 위함
- 즉, 템플릿을 구성하는 멤버함수 일부 혹은 전체를 모두 다르게 행동시킬 수 있음
Q. 문자열 비교할 때, 사전순이 맞을까? 아니면 길이순이 맞을까?
A: 이런 특수 상황에 따라 예외가 필요하기에 사용하는 것이 바로 템플릿의 특수화이다.
※ 함수 템플릿과 static 지역변수
함수템플릿을 기반, 컴파일러는 ‘템플릿 함수’들을 만들어 낸다.
따라서 static 지역변수도 템플릿 함수 별로 각각 존재하게 된다.
※ 클래스 템플릿과 static 멤버변수
static멤버변수는 변수가 선언된 클래스의 객체간 공유가 가능.
따라서 클래스별 static 멤버변수를 유지하게 된다.
※ 템플릿기반 템플릿 클래스 객체를 저장하는 객체 생성법
※ Advanced Template
python에서 다음과 같이 print함수를 사용할 때, 인자로 전달되는 것들을 모두 출력할 수 있다.
print(1, 3.1, "abc")
그렇다면 C++에서도 이와 같은 방법으로 출력에서 인자에 대한 사전정보가 없을 때, 출력을 조금 더 유동적으로 하는 방법은 없을까?
※ variadic template (가변길이 템플릿)
#include <iostream>
using namespace std;
template <typename T>
void print(T args) {
cout << args << endl;
}
template <typename T, typename... Types>
void print(T arg, Types... args) {
cout << arg << ", ";
print(args...);
}
int main() {
print(1, 3.1, "abc");
print(1, 2, 3, 4, 5, 6, 7);
}
/*************** 출력 ***************/
// 1, 3.1, abc
// 1, 2, 3, 4, 5, 6, 7
- parameter pack
template <typename T, typename... Types>
void print(T arg, Types... args) {
typename 뒤에 ...으로 오는 것을 템플릿 파라미터 팩이라 부른다.
템플릿 파라미터 팩은 0개 이상의 템플릿 인자들을 나타낸다.
§ Fold Expression
위에서 사용한 recursion진행 대신 사용하는 방법으로 variadic template에서 가변인자 처리를 위해 parameter pack을 사용하는데, 이때! C++17부터 fold expression이 제공되어 parameter pack을 더 쉽게 사용할 수 있게 된 것이다.
- 인자가 1개:
1) Unary right
(E op ...) ex_ (E1 op (... op (EN-1 op EN)))
2) Unary left
(... op E) ex_ (((E1 op E2) op ...) op EN)
unary right fold
E1 op ( ... op ( En-1 op En))
- 전달받은 parameter pack을 위와 같은 표현식으로 변경
- 뒤에 있는 인자 부터 먼저 순차적으로 연산해서 결과 값을 생성
template<typename... Args>
auto sum(Args... args) {
return (args + ...);
}
unary left fold
((E1 op E2) op ...) op En
- 전달받은 parameter pack을 위와 같은 표현식으로 변경
- 앞에 있는 인자 부터 순차적으로 연산해서 결과 값을 생성
template<typename... Args>
auto sum(Args... args) {
return (... + args);
}
- 인자가 2개
3) Binary right
(E op ... op I) ex_ (E1 op (... op (EN−1 op (EN op I))))
4) Binary left
(I op ... op E) ex_ ((((I op E1) op E2) op ...) op EN)
binary right
E op ... op I
- 전달받은 parameter pack을 위와 같은 표현식으로 변경
- 단항을 사용하는 문법과 다르게 parameter pack과 별개로 초기 연산할 값을 추가할 수 있다.
template<typename ...Args>
int sum(Args... args) {
return (args + ... + (1 * 2));
}
binary left
I op ... op E
- 전달받은 parameter pack을 위와 같은 표현식으로 변경
template<typename ...Args>
int sum(Args... args) {
return ((1 * 2) + ... + args);
}
§ Functor (Function Object, 함수객체)
다음과 같이 함수처럼 동작하는 클래스를 Functor, 함수객체라 부른다.
struct AddFunction {
int operator()(const int& num1, const int& num2) {
return num1 + num2;
}
double operator()(const double& num1, const double& num2) {
return num1 + num2;
}
};
int main() {
auto num1 = 1.32, num2 = 12.34;
AddFunction add;
auto result = add(num1, num2);
cout << result << endl;
return 0;
}
이런 functor는 기존의 함수보다 더 flexible 즉, 유연한데, 객체를 이용하기에 임의의 상태에 대한 정보를 전달가능하다.
functor를 사용하면 기능조작이 쉽고 컴파일러가 기능 자체를 inline화 시켜 매우 빠르게 작업할 수 있다.
1) Unary right
(E op ...) ex_ (E1 op (... op (EN-1 op EN)))
2) Unary left
(... op E) ex_ (((E1 op E2) op ...) op EN)
'C | C++ > C++' 카테고리의 다른 글
this.code(12)_ auto 키워드와 decltype & 범위기반 for문 (★★★★) (0) | 2022.10.29 |
---|---|
this.code(11)_ Exception Handling (예외 처리), Exception 클래스, noexcept (0) | 2022.10.29 |
this.code(9)_ smart pointer & type casting_ part 2. (0) | 2022.10.29 |
★this.code(8)_ type casting_ part 1. cast operator (★★★) (0) | 2022.10.29 |
this.code(7)_ operator overloading_part2. copy function (0) | 2022.10.27 |