※ 템플릿 함수와 함수 템플릿

- 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 멤버변수를 유지하게 된다.

 

좌) 함수 템플릿과 static 지역변수&nbsp; &nbsp; &nbsp;우) 클래스 템플릿과 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)

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts