Skip to main content

[C++ Primer Plus] Chapter 16. (4) STL - 함수 객체

Table of Contents

C++ 기초 플러스 책을 읽고 공부한 노트입니다.




함수 객체(functor) #

  • 펑크터
    • 함수처럼 ()와 함께 사용할 수 있는 객체이다.
    • 일반 함수, 함수를 지시하는 포인터, ()가 오버로딩된 클래스 객체가 모두 펑크터가 될 수 있다.
  • 펑크터 개념
    • 제너레이터(generator)
      • 매개변수 없이 호출하는 함수
    • 단항 함수(unary function)
      • 하나의 매개변수로 호출하는 함수
    • 이항 함수(binary function)
      • 두 개의 매개변수로 호출하는 함수
  • 펑크터 개념의 개량
    • 조건(predicate)
      • bool값을 리턴하는 단항 함수
    • 이항 조건(binary predicate)
      • bool값을 리턴하는 이항 함수

  • list는 하나의 조건을 매개변수로 받아들이는 remove_if() 멤버를 가진다.
    • 이것은 각 멤버에 조건을 적용해서 조건이 true를 리턴하는 모든 원소들을 삭제한다.
    • 기준과, 원소의 값 두 가지 매개변수를 전달하지 못한다.
    • 하지만 클래스 펑크터를 사용하면 기준이 되는 매개변수를 생성자로 미리 전달할 수 있다.
template<class T>  // 펑크터 클래스가 operator()()를 정의한다. 
class TooBig
{
private:
    T cutoff;

public:
    TooBig(const T& t) : cutoff(t) {}

    bool operator()(const T& v)
    {
        return cutoff < v; // cutoff보다 크면 true
    }
};

void OutInt(int n)
{
    cout << n << " ";
}

int main()
{
    TooBig<int> f100(100); // 100초과의 것들은 true
    
    list<int> yadayada = {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};
    list<int> etcetera = {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};

    cout << "원래의 리스트:\n";
    for_each(yadayada.begin(), yadayada.end(), OutInt);
    cout << endl;
    for_each(etcetera.begin(), etcetera.end(), OutInt);
    cout << endl;
 
    yadayada.remove_if(f100);              // 선언된 함수 객체 
    etcetera.remove_if(TooBig<int>(200));  // 익명의 함수 객체
    // 200초과의 것들은 true. 다 지운다. 

    cout << "정비된 리스트:\n";
    for_each(yadayada.begin(), yadayada.end(), OutInt);
    cout << endl;
    for_each(etcetera.begin(), etcetera.end(), OutInt);
    cout << endl;
}
원래의 리스트:
50 100 90 180 60 210 415 88 188 201
50 100 90 180 60 210 415 88 188 201
정비된 리스트:
50 100 90 60 88
50 100 90 180 60 88 188

  • 두 개의 매개변수를 사용하는 템플릿 함수를 이미 가지고 있다면,
    • 클래스를 사용해서 이것을 하나의 매개변수를 사용하는 함수 객체로 변환할 수 있다.
template<class T>
bool tooBig(const T& val, const T& lim)
{
    return val > lim;
}

template<class T>
class TooBig2  // 함수 어댑터이다. tooBig함수를 다른 인터페이스에 맞게 개조시킨다. 
{
private:
    T cutoff;

public:
    TooBig2(const T& t) : cutoff(t) {}

    bool operator()(const T& v)
    {
        return tooBig<T>(v, cutoff);  // tooBig 함수를 사용한다. 
    }
};

  • 미리 정의된 펑크터
    • STL은 몇 가지 기본적인 펑크터들을 정의한다. 그것들은 함수를 매개변수로 취하는 STL 함수들을 지원하기 위해 제공된다.
    • 예를 들어, transform()함수를 생각해 보자.
#include <functional>
//...

plus<int> add;           // plus<int> 객체를 생성한다. 
int result = add(1, 4);  // plus<int>::operator()()를 사용한다. 


vector<double> vec = { 36, 39, 42, 45, 48 };
vector<double> vec2 = { 1, 2, 3, 4, 5 };

ostream_iterator<double, char> out(cout, " ");

transform(vec.begin(), vec.end(), vec2.begin(), out, plus<double>());  
// 익명의 plus<double> 함수 객체를 만들어서 사용했다. 
// vec의 0~4와 vec2의 0~4의 원소를 각각 더해서 화면에 출력한다. 

  • 내장 연산자들과 동등한 펑크터들
    • 이것은 모두 순응성(adaptable)이다.
연산자 동등한 펑크터
+ plus
- minus
* multiplies
/ divides
% modulus
- negate
== equal_to
!= not_equal_to
> grater
< less
>= grater_equal
<= less_equal
&& logical_and
|| logical_or
! logical_not

  • 순응성(adaptable)
    • 매개변수형과 리턴형을 식별하는 typedef멤버를 가지는 것이다.
    • 그 멤버들은 result_type, first_argument_type, second_argument_type 등이 있다.
    • 예를 들어, plus<int> 객체의 리턴형은 plus<int>::result_type이다.

  • 펑크터가 순응성이면, 함수 어댑터 객체가 펑크터를 사용할 수 있다.
    • 예를 들어, multiplies는 이항 함수이다. 따라서 하나의 매개변수만 제공하는 아래와 같은 코드는 동작하지 않는다.
    • 그래서 두 개의 매개변수를 사용하는 펑크터를 하나의 매개변수를 사용하는 펑크터로 변환하는, 함수 어댑터가 필요하다.
    • STL은 순응성 이항 함수를 순응성 단항 함수로 변환하는 binder1stbinder2nd 클래스를 제공한다.
    • 그리고 이것을 간소화한 bind1st, bind2nd 함수를 제공한다.
      • bind1st는 제 1 매개변수를 제공한다.
      • bind2nd는 제 2 매개변수를 제공한다.
vector<double> vec = { 36, 39, 42, 45, 48 };

ostream_iterator<double, char> out(cout, " ");
transform(vec.begin(), vec.end(), out, multiplies<double>()); // (X)
// vec만 가지고는 곱셈을 할 수 없다. 

binder1st<multiplies<double>> b1 = binder1st<multiplies<double>>(multiplies<double>(), 2.5);
transform(vec.begin(), vec.end(), vec.begin(), b1);  // (O)
// binder1st 함수 어댑터 객체를 만들어서
// 이항 함수 multiplies를 단항 함수로 만들었다. 
    

transform(vec.begin(), vec.end(), out, bind1st(multiplies<double>(), 2.5)); // (O)
// bind1st 함수는 좀 더 간편하게 사용할 수 있다. 
// vec은 제 2 매개변수이다. 
// bind1st( 2항 함수, 제 1 매개변수 ) 
// vec의 원소들과 bind1st에 제공된 제 1 매개변수를 곱한값을 출력한다.