[C++ Primer Plus] Chapter 16. (4) STL - 함수 객체
Table of Contents
C++ 기초 플러스 책을 읽고 공부한 노트입니다.
함수 객체(functor) #
- 펑크터
- 함수처럼
()
와 함께 사용할 수 있는 객체이다. - 일반 함수, 함수를 지시하는 포인터,
()
가 오버로딩된 클래스 객체가 모두 펑크터가 될 수 있다.
- 함수처럼
- 펑크터 개념
- 제너레이터(generator)
- 매개변수 없이 호출하는 함수
- 단항 함수(unary function)
- 하나의 매개변수로 호출하는 함수
- 이항 함수(binary function)
- 두 개의 매개변수로 호출하는 함수
- 제너레이터(generator)
- 펑크터 개념의 개량
- 조건(predicate)
bool
값을 리턴하는 단항 함수
- 이항 조건(binary predicate)
bool
값을 리턴하는 이항 함수
- 조건(predicate)
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은 순응성 이항 함수를 순응성 단항 함수로 변환하는
binder1st
와binder2nd
클래스를 제공한다. - 그리고 이것을 간소화한
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 매개변수를 곱한값을 출력한다.