함수 객체, 템플릿

하루공부·2024년 1월 18일
0

C++

목록 보기
7/25
post-thumbnail

C++ 아이콘 제작자: Darius Dan - Flaticon


함수 객체

  • 함수는 아니지만 함수 처럼 작동하는 객체 Function Object => Functor

    함수 처럼 작동하는 객체 ==> 객체를 함수처럼 작동하게 만든다.

    • 이를 위해 operator() 연산자를 오버로딩해야한다 ==> 호출 연산자

      함수 객체를 호출하면 객체.operator()(인자값) 형태로 호출하지만 중간에 .operator가 생략
      ==> 이를 암묵적 호출이라 한다. 반면 위처럼 표현을 온전히 하는것은 명시적 호출이다.
      또한 암묵적 호출중 객체를 생성하지 않고 클래스()(인자값) 를 호출해 임시 객체를 통한 암묵적 호출도 있다

  • 왜 사용하나?

    장점1. 일반 함수와 달리 호출 연산자 이외 데이터를 저장할 변수를 만들어 필요한 속성, 상태를 포함할 수 있다.

    장점2. 함수객체의 생성과 실행 시점이 다르다

    일반 함수는 호출시 바로 실행되지만 함수 객체는 생겅한다고 실행되는게 아니다.

==> 함수 객체를 함수에 전달해서 사용이 가능하다.

가령 나만의 컨테이너를 만들고 정렬 함수를 만들었다 치자.
그런데 자연스럽게 오름차 순으로 함수를 만들었고 후에 내림차 순의 함수까지 만들고자 할 때
기존의 함수에서 함수를 받는 인자를 추가한다
받은 함수에서 원소 끼리 비교를 수행하여 크면 1 작으면 0 결과값을 반환하고
이에 맞게 정렬을 한다.




template

  • 템플릿에 인자를 전달해서 실제 코드를 생성하는 것을 클래스 템플릿 인스턴스화 라고 한다.



함수 템플릿

  • 템플릿에 전달 받은 인자로 함수를 만드는 템플릿 ==> 함수를 만드는데 사용되는 템플릿
    template <class T>
    T Add(T a, T b) {
    	return a + b;
    }
    int main() {
    	std::cout << Add(1, 2);
    }
  • 클래스 템플릿과 달리 함수 템플릿를 호출할 때<> 없이 컴파일이 된다 인수의 타입을 보고 알아서 <> 작동시켜 인스턴스화를 해준다.
    ==> 전달되는 인자를 보고 컴파일러가 타입을 정한다.

템플릿 함수

  • 함수 템플릿으로 생성된 함수.
  • 위 예제로 예시로 아래가 템플릿 함수에 해당한다.
int add<int>(int a, int b){
	return a+b;
}


템플릿 특수화

  • 어떤 특정한 상황에서 별도로 작업을 수행하기 위해 특별히 따로 템플릿을 만들어줌
template <typename A, typename B, typename C> 
class test {}; ==> A가 int형이고 C가 double형일 때 다른 방식으로 처리하고 싶다!!
template <typename B>
class test<int, B, double> {};
  • 위 처럼 특수화를 시키기 특수화 하려는 타입을 전달하고 위에는 일반적인 템플릿을 받으면 된다.

    만약 템플릿 인자가 없더라도 특수화를 하고 싶으면 template<>빈칸 이라도 남겨줘야한다




디폴트 템플릿 인자

  • 전달된 비교하는 템플릿 함수를 만든다 생각.
  • 자신 만의 클래스 같은 경우는 비교 연산자가 정의되어 있지 않아 정의가 필요한데
    모든 클래스에 비교 연산자를 만들 수 없으니 비교하는 함수를 만들어 템플릿 함수에 전달.
  • 하지만 위에서 말한 비교 함수에 템플릿 함수를 호출할 때 마다 타입을 전달해야 한다. => 귀찮다.
  • 이것을 아래 예시 처럼 디폴트 인자를 사용해 간단하게 사용이 가능
template <typename T, typename Comp = Compare<T>>
T Min(T a, T b){ ... }
Min(a, b) ==> main함수에서 사용함 //원래라면 Min<int, Compare<int>>(a, b) 



가변 길이 템플릿

  • 임의의 개수의 인자를 받는 함수를 만들 수 있다.

    template (typename arg, typename... args)
    return_type function_name(arg var1, args... var2)
    template <typename T, typename... Types>
    void print(T arg, Types... args) {
    std::cout << arg << ", ";
    print(args...);
    }
  • ... 으로 표시한 것이 parameter pack이라고 한다.

    템플릿 파라미터 팩, 함수 파라미터 팩
    ==> 0개 이상의 템플릿 인자와 0개 이상의 함수 인자를 표현

    위 print 템플릿 함수 예시로 printf(1, 2.2, "3"); 을 호출하면
    인자가 여러개여서 가변 길이 템플릿 함수가 호출되고, T에는 첫 번째 매개변수인 1의 int타입 추론되고 arg는 1이 된다.
    그리고 나머지는 args가 된다.
    그후 재귀적으로 다시 함수가 호출되면서 args값이 전달된다.

  • 만약 인자가 1개이면 c++규칙상 파라미터 팩이 없는 함수의 우선순위가 더 높아서 평범한 함수가 호출된다.


  • sizeof... 연산자는 파라미터 펙이 있는 곳 안에서 사용하면 넘겨 받은 파리미터 인자들의 모든 갯수를 리턴

    1개의 변수를 만들고 sizeof...() 그 변수를 전달하면 넘겨 받은 파라미터 인자들의 모든 갯수를 리턴.


  • 하지만 이런 가변 길이 템플릿은 아주 편리하지만 1가지 단점인 재귀 함수 형ㄹ태로 구성해야 한다는 점이다. ==> 재귀를 종료시킬 함수를 따로 만들어 줘야한다 => 귀찮고 복잡해짐
    ==> C++17에 생긴 Fold형식을 사용하면 재귀형식을 사용하지 않고 간단하게 표현할 수 있다.
    template <typename... Ints>
    int sum_all(Ints... nums) {
    return (... + nums);
    }
    ==> return (... + nums); 이것이 Fold형식으로 컴파일은 return ((((1 + 4) + 2) + 3) + 10);으로 해석한다.

  • 4가지 형태의 Fold 표현식이 존재한다. init는 초기값 pack은 파라미터 펙으로 전달된 인자.

    ==> op에는 이항 연산자들이 들어가고 1가지 중요한 점은 ()로 감싸주지 않으면 컴파일 오류가 난다.

    E는 파라미터 펙에 전달된 인자
    1) Fold (E1 op (... op (EN−1 op EN )))
    2) Fold (((E1 op E2) op ...) op EN )
    3) Fold (E1 op (... op (EN−1 op (EN op init))))
    4) Fold ((((init op E1) op E2) op ...) op EN


참조
참조
참조
공부한 내용 복습

개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!

0개의 댓글