C++ / / 2025. 3. 13.

[C++] 함수 객체( function object, functor ) : operator()를 구현한 타입

반응형

함수 객체( function object )

함수 객체( function object )는 operator() 함수를 구현한 타입( type )을 말합니다.

C++ 표준 라이브러리에서는, 펑터( functor )라고도 불리는, 이 타입을 주로 컨테이너나 알고리즘의 정렬 기준이 필요할 때 사용합니다.

 

다음 예제에서는 이러한 함수 객체를 사용하는 법을 볼 수 있습니다.

class FuncObj{	

public:
    int operator()(int a, int b){	// 이 연산자를 구현한 타입이 함수 객체
        return a + b;
    }
};

int Func(int a, int b){	// 일반 함수
    return a + b;
}

int main(){

    FuncObj func;			// 함수 객체의 변수 선언
    int a = 5;
    int b = 6;

    int sum1 = func(a, b);	// operator()를 호출
    
    int sum2 = Func(a, b);	// 일반 함수 호출
}

위에서 보듯이, 함수 객체의 변수명 func()를 사용해서, 이 func 객체의 operator()를 호출할 수 있습니다.
이것이 일반 함수를 사용하는 법과 유사하기 때문에, FuncObj 타입을 함수 객체라고 부르는 이유입니다.

( 참고로, 일반 함수를 호출할 때도, () 연산자의 피연산자로 함수명을 사용합니다. )


그렇지만, 일반 함수보다 이러한 함수 객체( function object )를 사용할 때는 다음과 같은 장점이 있습니다.
첫 번째, 함수 객체는 상태 정보를 담을 수 있습니다.
두 번째, 함수 객체는 타입( type )이므로, 템플릿의 매개 변수가 될 수 있습니다.

 

함수 객체의 상태 정보

다음 예문은 배열의 원소들의 값을 3씩 증가시키는 코드입니다.

#include <algorithm>
using namespace std;

int increment(int n){	// transform에 전달될 함수
    return n + 3;
}

int main(){

    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    int res[n];
 
    std::transform(arr, arr+n, res, increment);
 
    for (int x : res){
        cout << x << " ";
    }

    return 0;
}

여기에 쓰인 std::transform 함수는 arr 배열의 처음부터 끝까지의 원소를 대상으로 increment 함수를 호출하여, 그 결과를 res 배열에 저장하는 함수입니다.

그런데, 이 increment 함수는 매개 변수를 하나밖에 사용하지 않습니다.
왜냐하면, transform이 매개 변수를 하나만 갖는 함수를 필요로 하기 때문입니다.

 

그래서, 만약 배열의 원소에 3 대신 5를 더하고 싶다면, increment 함수를 다시 작성해야 합니다.
아니면, 전역 변수를 사용해서 추가적인 정보를 제공하는 방법을 써야 합니다.

그러나, 함수 객체( function object )는 increment 함수에 필요한 정보들을 다양한 방법으로 가질 수 있습니다.

class incrementObj{		// 함수 객체
    int m_increment;	// 상태 정보
public:

    incrementObj(int incre) : m_increment(incre){}

    int operator()(int n){	// () 연산자
        return n + m_increment;
    }
};

int main(){

    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    int res[n];
    int amount = 5;	// 배열에 더하고 싶은 값

    std::transform(arr, arr+n, res, incrementObj(amount));  // 함수 객체 사용
 
    for (int x : res){
        cout << x << " ";
    }

    return 0;
}

위와 같이, 함수 객체 incrementObj의 생성자를 통해서 필요한 정보를 제공하거나, 멤버 함수를 통해서 정보를 넣을 수도 있습니다.

참고로, std::transform 함수는 매개 변수를 두 개 갖는 함수를 사용하는 버전이 있습니다.
예를 들어, 두 배열의 값을 더한 결과를 다른 배열에 저장하는 기능이 필요할 때 이 버전을 사용합니다.

 

[C++] 입력된 원소들을 차례로 변환하는 std::transform

std::transform 기본 사용법std::transform은 지정된 함수 객체를 입력된 범위의 모든 원소들에게 적용하고, 그 결과를 대상 범위에 출력하는 함수입니다. 이 함수를 사용하려면 먼저 다음 헤더를

codingbonfire.tistory.com

 

 

템플릿( template )의 매개 변수로서의 함수 객체

C++ Standard Library는 다양한 함수 객체( function object )를 <functional> 헤더 파일을 통해서 제공하고 있습니다.

 

그리고, 이런 함수 객체들을 사용해서 표준 라이브러리 컨테이너의 목표를 달성하고 있습니다.

예를 들어, std::set의 선언은 다음과 같습니다.

template <class Key,
    class Traits=less<Key>,	// less는 함수 객체
    class Allocator=allocator<Key>>
class set

이 중, 두 번째 매개 변수가 std::set 컨테이너의 원소를 정렬하는 데 사용되는 함수 객체입니다.

 

less 함수 객체의 선언은 다음과 같습니다.

template <class Type = void>
struct less : public binary_function <Type, Type, bool>
{
    bool operator()(const Type& Left, const Type& Right) const;
};

이 함수 객체는 매개 변수 LeftRight보다 작으면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
이 반환 정보를 사용해서 std::set은 원소들을 정렬합니다.

 

물론, 다른 식으로 원소들을 정렬할 수도 있습니다.
예를 들어, 내림차순으로 정렬하고 싶다면, <functional> 파일이 제공하는 greater 함수 객체를 사용할 수 있습니다.
또는, 사용자 함수 객체를 작성해, 필요에 따른 정렬 방식을 제공할 수도 있습니다.

template<typename T>    // 사용자 함수 객체
struct compObj{
    bool operator()( const T& a, const T& b){
        return a > b;	// 내림차순으로 정렬
    }
};

int main(){

    std::set< int, compObj<int> > s;	// 함수 객체를 인자로 전달
    s.insert(3);
    s.insert(5);
    s.insert(7);

    return 0;
}

위와 같이 함수 객체( 여기서는 compObj<int> 타입 )를 std::set의 원소를 정렬하는 객체의 타입으로 전달하는 대신, 함수를 사용할 수도 있는데, 약간 더 번거롭습니다.
이 때는, 아래와 같이 함수의 타입을 두 번째 인수로 넘기고, 사용될 구체적인 함수를 std::set의 생성자를 통해서 알려줘야 합니다.

template<typename T>
bool comp( const T& a, const T& b){	// 일반 비교 함수
    return a > b;
}

template<typename T>
using CompType = bool(*)(const T&, const T&);	// 비교 함수 타입

int main(){

    set< int,  CompType<int> > s(comp<int>);	// 함수 객체와 함수 포인터 전달
    s.insert(3);
    s.insert(5);
    s.insert(7);
}

 

C++ 11에 도입된 람다( lamda ) 함수를 사용하면 일반 함수를 사용하는 것보단 편리해집니다.

template<typename T>
auto lamda = [](const T&a, const T&b){	// 람다 함수
    return a > b;
};

int main(){

    set< int, decltype(lamda<int>) > s(lamda<int>);
    s.insert(3);
    s.insert(5);
    s.insert(7);
}

람다 함수의 타입은 컴파일러만 알 수 있는 고유한 타입이기 때문에, decltype 키워드를 사용해서 람다의 타입을 알려주어야 합니다.

 

[C++] 간단한 함수 객체를 정의하기 위한 람다 표현식( lamda expression )

람다 표현식( lamda expression )줄여서 람다( lamda )라고도 하는 람다 표현식은 익명의 함수 객체를 정의하고 사용하기 위한 표기법입니다. 이 표현식은 간단한 기능을 구현하는데, 너무 많은 손이 가

codingbonfire.tistory.com

 

알고리즘에서 함수 객체 사용

std::sort 함수는 컨테이너나 배열의 원소를 원하는 방식으로 정렬하는 함수입니다.

이 함수는 정렬을 원하는 부분을 정하는 매개 변수와 두 원소를 비교하는 함수를 인자로 받는 함수입니다.
이때도, 두 원소를 비교하는 함수 대신 같은 역할을 하는 함수 객체( function object )를 사용할 수 있습니다.

다음 예제에서, 내림차순으로 정렬하는 함수 객체 greater를 볼 수 있습니다.
이 객체도 <functional> 헤더 파일에서 볼 수 있습니다.

#include <iostream>
#include <algorithm>
using namespace std;

int main(){

    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    sort( arr, arr + n, greater<int>());    // 함수 객체를 전달
 
    for( int x : arr){
        cout << x << " ";
    }
}

▼출력

5 4 3 2 1

이 때는, 함수 객체( 타입 )의 객체를 요구하기 때문에, greater<int>가 아니라, greater<int>()std::sort 함수에 인수로 알려줘야 합니다.

 

[C++] 배열, 컨테이터, 사용자 정의 데이터를 정렬하기 위한 std::sort

std::sort의 정의와 기초 사용법std::sort는 지정된 범위에 있는 데이터 요소를 기본적인 오름차순 또는 지정한 정렬 기준에 따라 정렬하는 함수입니다.이 함수를 사용하기 위해서는 우선 다음과 같

codingbonfire.tistory.com

 

 

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유