Templates

강한친구·2022년 3월 4일

C / CPP

목록 보기
9/19

강의를 보고 배운내용을 정리하였습니다.

틀 만들기

    class Vector {
        string* data;
        int capacity;
        int length;

        public:
        Vector(int n = 1);

        void push_back(string s);
        string operator [] (int i);
        void remove(int x);
        int size();
        ~Vector();
    };

다음과 같은 코드가 있다고 하자. 이는 string 자료형에 대응하는 벡터이다. 만약 이를 다른 자료형으로 쓰려한다면, class를 다시 선언해야 할 것이다.

코딩할 때 제일 많이 듣는 말중 하나가 중복을 줄이자 이다. 이러한 중복을 줄이기 위해서 나온 방법이 바로 template이다. 틀을 만들어놓고 빈자리에 필요한 값만 채워서 바로 넣어버리는 방식이다.

templates

# include <iostream>
# include <string>

template <typename T>
    class Vector {
    T* data;
    int capacity;
    int length;

    public:
    Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}

    void push_back(T s) {
        if (capacity <= length) {
        T* temp = new T[capacity * 2];
        for (int i = 0; i < length; i++) {
            temp[i] = data[i];
        }
        delete[] data;
        data = temp;
        capacity *= 2;
        }

        data[length] = s;
        length++;
    }

    T operator[](int i) { return data[i]; }

    void remove(int x) {
        for (int i = x + 1; i < length; i++) {
        data[i - 1] = data[i];
        }
        length--;
    }

    int size() { return length; }

    ~Vector() {
        if (data) {
        delete[] data;
        }
    }
};

template < typename T >를 통해서 이러한 틀을 만들어 줄 수 있다. 이 T자리에 자료형을 넣어주면 되는것이다.

int main() {
    Vector<int> int_vac;
    int_vac.push_back(3);
    int_vac.push_back(2);
    
    std::cout << " int Vecotr " << std::endl;
    std::cout << "First Element : " << int_vac[0] << std::endl;
    std::cout << "Second Element : " << int_vac[1] << std::endl;

    Vector<std::string> str_vec;
    str_vec.push_back("hello");
    str_vec.push_back("world"); 
    std::cout << " std::string vector " << std::endl;
    std::cout << "First Element : " << str_vec[0] << std::endl;
    std::cout << "Second Element : " << str_vec[1] << std::endl;
}

이런식으로 작성하게 되면, int_vac 은 int형 자료를, str_vac은 str 자료를 가지게 된다.

특수화

만약

template <typename A, typename B, typename C> 
class Specialization {]

이라는 템플릿이 있다고 하자. 이때, A는 int 형, C는 string 이런식으로 따로 처리를 원한다면

template <typename B> 
class Specialization <int, B, string> {]

같은 식으로 따로 지정을 해주면 된다. (위에는 범용 템플릿을 써줌

만약 따로 템플릿 전달 인자를 설정하지 않았더라도

template <> 
class Specialization <bool> {]

template 선언은 해줘야한다.

함수 template

#include <iostream>
#include <string>

template <typename T>
T max(T& a, T& b) {
    return a > b ? a : b;
}

int main() {
    int a = 1; 
    int b = 2;
    std::cout << "Max : " << max(a, b) << std::endl;

    double c = 1.0;
    double d = 2.0;
    std::cout << "Max : " << max(c, d) << std::endl;
}

함수 역시 비슷한 방식으로 template화 할 수있다.

이를 가지고 bubblesort를 구현할 수 있다.

# include <iostream>
# include <string>

template <typename T>
    class Vector {
    T* data;
    int capacity;
    int length;

    public:
    Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}

    void push_back(T s) {
        if (capacity <= length) {
        T* temp = new T[capacity * 2];
        for (int i = 0; i < length; i++) {
            temp[i] = data[i];
        }
        delete[] data;
        data = temp;
        capacity *= 2;
        }

        data[length] = s;
        length++;
    }

    T operator[](int i) { return data[i]; }

    void remove(int x) {
        for (int i = x + 1; i < length; i++) {
        data[i - 1] = data[i];
        }
        length--;
    }

    int size() { return length; }

    void swap(int i, int j) {
        T temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    ~Vector() {
        if (data) {
        delete[] data;
        }
    }
};

template <typename Cont>
void bubble_sort(Cont& cont) {
    for (int i = 0; i < cont.size(); i++) {
        for (int j = i + 1; j < cont.size(); j++) {
            if (cont[i] > cont[j]) {
                cont.swap(i, j);
            }
        }
    }
}

int main() {
    Vector<int> int_vec;

    int_vec.push_back(3);
    int_vec.push_back(4);
    int_vec.push_back(5);
    int_vec.push_back(8);
    int_vec.push_back(9);
    int_vec.push_back(2);

    std::cout << "before Sorting " << std::endl;
    for (int i = 0; i < int_vec.size(); i++) {
        std::cout << int_vec[i] << " ";
    }
    std::cout << std::endl;

    bubble_sort(int_vec);

    std::cout << "before Sorting " << std::endl;
    for (int i = 0; i < int_vec.size(); i++) {
        std::cout << int_vec[i] << " ";
    }
    std::cout << std::endl;
}

위의 vector는 기존의 쓰던 vector와 동일하지만, bubblesort에 사용하기위해 swap함수를 추가로 구현하였다.

이런 bubblesort를 역순정렬, 혹은 다른 기준으로 바꿔서 정렬하려면 어떻게 해야할까?

이럴때 사용하는 방법이 함수 객체이다.

Function-Object

# include <iostream>
# include <string>

template <typename T>
    class Vector {
    T* data;
    int capacity;
    int length;

    public:
    Vector(int n = 1) : data(new T[n]), capacity(n), length(0) {}

    void push_back(T s) {
        if (capacity <= length) {
        T* temp = new T[capacity * 2];
        for (int i = 0; i < length; i++) {
            temp[i] = data[i];
        }
        delete[] data;
        data = temp;
        capacity *= 2;
        }

        data[length] = s;
        length++;
    }

    T operator[](int i) { return data[i]; }

    void remove(int x) {
        for (int i = x + 1; i < length; i++) {
        data[i - 1] = data[i];
        }
        length--;
    }

    int size() { return length; }

    void swap(int i, int j) {
        T temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    ~Vector() {
        if (data) {
        delete[] data;
        }
    }
};

template <typename Cont, typename Comp>
void bubble_sort(Cont& cont, Comp& comp) {
    for (int i = 0; i < cont.size(); i++) {
        for (int j = i + 1; j < cont.size(); j++) {
            if (!comp(cont[i], cont[j])) {
                cont.swap(i, j);
            }
        }
    }
}

struct comp1 {
    bool operator()(int a, int b) { return a > b; }
};
struct comp2 {
    bool operator()(int a, int b) { return a < b; }
};

int main() {
    comp1 comp1;
    comp2 comp2;
    
    Vector<int> int_vec;

    int_vec.push_back(3);
    int_vec.push_back(4);
    int_vec.push_back(5);
    int_vec.push_back(8);
    int_vec.push_back(9);
    int_vec.push_back(2);

    std::cout << "before Sorting " << std::endl;
    for (int i = 0; i < int_vec.size(); i++) {
        std::cout << int_vec[i] << " ";
    }
    std::cout << std::endl;

    bubble_sort(int_vec, comp1);

    std::cout << "after ASC Sorting " << std::endl;
    for (int i = 0; i < int_vec.size(); i++) {
        std::cout << int_vec[i] << " ";
    }
    std::cout << std::endl;

    bubble_sort(int_vec, comp2);

    std::cout << "after DESC Sorting " << std::endl;
    for (int i = 0; i < int_vec.size(); i++) {
        std::cout << int_vec[i] << " ";
    }
    std::cout << std::endl;
}

buublesort함수를 잘 보면 기존의 단순 부등호 비교에서, comp라는 함수를 통해 비교를 수행하는것을 알 수 있다.

comp는 그 내부에서 연산자 ()를 정의하고있다. 즉, 실제로는 함수가 아니다. 하지만 bubblesort안에서는 함수처럼 사용되고 있다.

이러한 것들을 함수 객체(Function Object 혹은 Functor 라고 부른다.)

`

타입이 아닌 템플릿 인자

#include <iostream>

template <typename T, int num>
T add_num(T t) {
  return t + num;
}

int main() {
  int x = 3;
  std::cout << "x : " << add_num<int, 5>(x) << std::endl;
}

위의 코드를 잘 살펴보면, template가 int num을 받고 아래의 함수가 이를 인자처럼 쓰는것을 볼 수 있다.

이런식으로 타입이 아닌 인자로 사용될 수 있는 자료형은 제한적으로 존재한다.

  • 정수타입 (bool, int, char, long)
  • 포인터 타입
  • enum 타입
  • std::nullptr_t

기존의 케이스에서는 배열을 전달하면 배열의 크기를 잃어버리곤했지만, 새로 생긴 #include < array >를 이용하면 문제없이 사용할 수 있다.

디폴트 인자

처음에 인자에 기본값을 줄 수 도 있다.

#include <iostream>

template <typename T, int num = 3>
T add_num(T t) {
  return t + num;
}

int main() {
  int x = 3;
  std::cout << "x : " << add_num<int>(x) << std::endl;
}

이런식으로 작성하면 6이라는 결과를 받을 수 있다.

혹은 Compare를 쓰지 않고 대소비교를 하기 위해 이런식으로도 작성할 수 있다.

template <typename T, typename Comp>
T min(T a, T b) {
	Comp comp;
    if (comp(a, b)) {
    	return a;
    }
    return b;
}

0개의 댓글