[Basic C++] #48_함수 객체 어댑터, 바인더, not_1(), not_2(), mem_fn()
STL 알고리즘 중 "함수 객체 어댑터"에 대해 알아보겠습니다.
"전문가를 위한 C"의 17 항목, "STL 알고리즘 마스터하기"에 해당하는 내용입니다.
함수 객체 어댑터
C++에서 제공하는 함수 객체를 보다 다방면에서 활용하기 위해 우리는 "함수 객체 어댑터"를 사용합니다.
함수 객체가 주어진 인터페이스와 형태가 다를 경우, 우리는 함수 객체를 단독으로 사용할 수 없습니다.
예를 들면, find_if의 프레디킷 함수는 하나의 인자만 넘겨받지만, "less<>()" 비교 함수 객체는 두 개의 인자를 필요로 하기 때문에 우리는 이때 함수 객체 어댑터를 활용합니다.
코드를 통해 자세한 내용을 살펴보겠습니다.
바인딩
#include <iostream>
#include <string>
#include <functional>
using namespace std;
void func(int num, const std::string& str)
{
cout << num << " " << str << endl;
}
int main()
{
std::string myStr = "Hello";
// std::placeholders::_1 : 묶어둘 함수 인자의 첫 번재 인자를 첫 번재 인자로 설정합니다.
auto f = bind(func, std::placeholders::_1, myStr);
f(16);
// std::placeholders::_2 : 묶어둘 함수 인자의 두 번째 인자를 첫 번째 인자로 설정합니다.
auto f1 = bind(func, std::placeholders::_2, std::placeholders::_1);
f1("Test", 16);
}
"바인더"는 함수의 특정 파라미터를 특정 값으로 묶어둘 수 있습니다!
<functional>에 정의된 "std::bind()"는 함수의 특정 파라미터를 특정 값에 묶어둘 수 있게 해 줍니다.
위 예제 코드를 살펴보면, "f()" 메서드는 "bind()"를 통해 첫 번째 인자는 "std::placeholders::_1", 그리고 두 번째 인자는 앞서 선언한 "myStr"로 묶어둡니다.
이때, "std::placeholders::_1"은 "func()" 메서드의 첫 번째 인자가 "f()" 메서드의 첫 번째 인자로 위치합니다.
더불어, "std::placeholders::_2"는 "func()" 메서드의 두 번째 인자를 "f1()" 메서드의 첫 번째 인자로 위치합니다!
+ 참조형 바인딩
#include <iostream>
#include <string>
#include <functional>
using namespace std;
void increment(int& i) { ++i; }
int main()
{
int num = 0;
auto incr = bind(increment, num);
incr();
cout << num;
}
위 코드의 경우 "num"의 결과 값은 0입니다!
왜냐하면, "num" 변수는 bind를 통해 넘겨지는 과정에서 "복제에 의한 전달"이 이루어지기 때문입니다.
이때, 사용할 수 있는 메서드는 "ref()" 혹은 "cref()"로 바인딩 과정에서 "참조에 의한 전달"을 수행합니다!
+ 오버로딩 함수의 경우
#include <iostream>
#include <string>
#include <functional>
using namespace std;
void func(double& num) { ++num; }
void func(int& num) { ++num; }
int main()
{
int num = 0;
// auto f = bind(func, num); // 에러!!
auto f = bind((void(*)(double&))func, ref(num));
f();
cout << num;
}
오버로딩된 함수들의 경우 "bind()"와 함께 사용하면 문제가 발생합니다!
따라서, 두 개의 오버로딩 함수들 중 어느 것을 사용할 것인지 명시적으로 지정해야 합니다!
위 코드를 살펴보면, "(void(*)(double&)) func"처럼 함수 타입을 명시적으로 지정하여 "bind()"와 함께 사용합니다!
코드 예제
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main()
{
vector<int> v{ 1, 2, 3, 4, 5, 6 };
auto it = find_if(cbegin(v), cend(v),
bind(greater_equal<>(), placeholders::_1, 5));
while (it != end(v))
{
cout << *it << " is greater than 5" << "\n";
++it;
}
}
* 결과 화면
위 코드 예제는 "find_if()" 메서드의 Predicate 함수로 "less<>()", 비교 함수 객체를 사용하는 예제입니다.
"less<>()" 비교 함수 객체는 두 개의 인자를 필요로 하지만, Predicate 함수는 한 개의 인자만 받도록 인터페이스 설정이 되어있죠!
따라서, 우리는 앞서 공부한 바인딩을 통해 vector 컨테이너를 순회하며 인자로 받을 항목을 "less<>()" 함수 객체의 첫 번째 인자로 위치시키고, 항목들과 비교할 두 번째 인자는 "100"으로 묶어둠으로써 인터페이스 불일치 문제를 해결하고 있습니다!
not1(), not2()
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main()
{
vector<int> v{ 10, 20, 30, 100, 40 };
// 람다 표현식으로 작성된 Predicate
auto perfectScore = [](int i) {return i >= 100; };
function<bool(int)> f = perfectScore;
auto it = find_if(begin(v), end(v), not1(f));
if (it != end(v))
cout << *it << endl;
}
*결과 화면
"not1()"과 "not2()"는 부정 연산 어댑터입니다.
"not1()" 혹은 "not2()" 메서드는 단항 함수 혹은 이항 함수의 결과를 인자로 받아 결과 값을 반전시킵니다.
mem_fn(), 멤버 함수 호출
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
int main()
{
vector<string> str{"one", "", "three", "four"};
auto it = find_if(cbegin(str), cend(str), mem_fn(&string::empty));
if (it != end(str))
cout << "Empty at: " << static_cast<int> (it - begin(str)) << endl;
else
cout << "No Empty String!" << endl;
}
STL 컨테이너에 객체를 담고 있을 경우 제네릭 알고리즘에 넘겨줄 콜백 함수 혹은 프레디킷으로 객체의 멤버 메서드를 이용하고 싶을 때가 있습니다.
이때, 우리는 클래스의 멤버 메서드를 "mem_fn()" 사용합니다!
* 주의: "&string::empty()"의 함수 지정자 부분, "&string::"을 생략할 수 없습니다!
'언어 > Basic C++' 카테고리의 다른 글
[Basic C++] #50-1_STL 알고리즘, 변경 순차 알고리즘, transform(), copy(), copy_if(), copy_n() (0) | 2022.07.15 |
---|---|
[Basic C++] #49_STL 알고리즘, 불변 순차 알고리즘 (0) | 2022.07.13 |
[Basic C++] #47_함수 객체 (0) | 2022.07.08 |
[Basic C++] #46_람다 표현식과 STL 알고리즘 (0) | 2022.07.07 |
[Basic C++] #45_람다 표현식 활용, 제네릭 람다 표현식, 람다 캡처 표현식, 리턴 타입으로서 람다 표현식 (0) | 2022.07.06 |