언어/Basic C++

[Basic C++] #47_함수 객체

Hardii2 2022. 7. 8. 03:25

 

[Basic C++] #47_함수 객체

 

STL 알고리즘 중 "함수 객체"에 대해 알아보겠습니다.

"전문가를 위한 C"의 17 항목, "STL 알고리즘 마스터하기"에 해당하는 내용입니다.

 

 


 

Overview

 

  1. 개념
  2. 산술 함수 객체
  3. 비교 함수 객체
  4. 논리 함수 객체
  5. 구조체 오버로딩
  6. 람다 함수 객체

 

#0. 개념

1. 개념

  • 함수 객체는 함수처럼 동작하는 객체를 의미합니다. C++에서는 주로 Predicate를 표현하거나, 콜백 함수로 활용합니다.
  • <functional> 헤더에 정의된 여러 함수 객체 클래스들을 활용하여 "콜백" 작업을 수행할 수 있습니다. 

 

#1. 산술 함수 객체

1. 코드

#include <iostream>
#include <vector>
#include <numeric>
#include <functional>
using namespace std;

int main()
{
	vector<int> v{ 1, 2, 3, 4, 5 };
    	// multiplies< /* typename 생략 */ >() : 투과적 함수 객체 연산자 사용
	int res = accumulate(cbegin(v), cend(v), 0, multiplies<>());

	cout << res << endl;
}

 

Details

 

  • C++ 다섯 가지 이항 연산자에 함수 객체 템플릿을 제공합니다 - plus, minus, multiplies, divides, modulus.
  • 간단하게 "operator+" 등을 사용할 수 있지만, 함수 객체를 사용하여 알고리즘에 콜백으로 전달할 수 있기 때문입니다!

 

#2. 비교 함수 객체

1. 코드

#include <iostream>
#include <queue>
#include <functional>
using namespace std;

int main()
{
	// 디폴트 비교 연산 less<>() 에서 greater<>()로 변경
	priority_queue<int, vector<int>, greater<>> q;

	q.push(3);
	q.push(4);
	q.push(5);
	q.push(6);
	q.push(7);

	while (!q.empty())
	{
		cout << q.top() << " ";
		q.pop();
	}
}

 

Details

 

  • 산술 함수 객체와 더불어 C++는 비교 함수 객체 클래스 또한 제공하고 있습니다 - equal_to, not_equal_to, greater, less_equal, greater_equal.
  • 위 코드 예제는 "priority_queue"의 디폴트 정렬  순서 "오름차순(less)"에서 "내림차순(greater)"로 변경하고 있습니다. 

 

#3. 논리 함수 객체

1. 코드

#include <iostream>
#include <vector>
#include <numeric>
#include <functional>
using namespace std;

int main()
{
	vector<bool> v{ true, false, true, false };

	auto res = accumulate(cbegin(v), cend(v), true, logical_and<>());

	cout << res << endl;
}

 

Details

 

  • C++는 세 가지 논리 함수 객체 또한 제공합니다 - logical_or, logical_and, logical_not.

 

#4. 구조체 오버로딩

1. 예제-1

#include <iostream>
#include <queue>

struct CompareLength {
    bool operator()(const std::string& str1, const std::string& str2) const {
        return str1.length() < str2.length();
    }
};

int main() {
    std::priority_queue<std::string, std::vector<std::string>, CompareLength> pq;

    pq.push("apple");
    pq.push("banana");
    pq.push("cherry");
    pq.push("date");

    while (!pq.empty()) {
        std::cout << pq.top() << std::endl;
        pq.pop();
    }

    return 0;
}

 

Details

 

  • 구조체 오버로딩을 활용하는 대표적인 예제는 C++의 priority_queue(우선순위 큐) 선언 시 함수 객체로 사용하는 방법입니다.

 

2. 예제-2

#include <iostream>
#include <algorithm>
#include <vector>

struct GreaterThan {
    bool operator()(int a, int b) const {
        return a > b;
    }
};

int main() {
    std::vector<int> numbers = {5, 2, 9, 1, 5, 6};

    std::sort(numbers.begin(), numbers.end(), GreaterThan());

    for (int num : numbers) {
        std::cout << num << " ";
    }

    return 0;
}

 

Details

 

  • Predicate은 알고리즘 함수나 컨테이너의 멤버 함수와 함께 사용되는 함수 혹은 함수 객체입니다.
  • sort 알고리즘은 선택적으로 Predicate을 인자로 전달해, 그 정렬 방식을 정의할 수 있습니다.
  • 이때, 구조체 오버로딩 방식을 통해 Predicate을 정의할 수 있습니다.

 

#5. 람다 함수 객체

1. 예제-1

#include <iostream>
#include <queue>
#include <string>
#include <functional>
using namespace std;

int main() {

	auto Predicate = [](const string& a, const string& b) {return a > b; };

	// #1. decltype 활용
	priority_queue<string, vector<string>, decltype(Predicate) > pq;
	// #2. std::function 활용
	priority_queue < string, vector<string>, function<bool(const string& a, const string& b)>> pq2(Predicate);

	pq.push("apple");
	pq.push("banana");
	pq.push("cherry");
	pq.push("date");

	while (!pq.empty()) {
		std::cout << pq.top() << std::endl;
		pq.pop();
	}

	return 0;
}

 

Details

 

  • 우선 순위 큐 선언 시 필요한 Predicate를 람다 함수를 통해 정의할 수 있습니다.
  • 첫 번째 우선순위 큐 선언 방법은 decltype을 활용합니다. decltype은 함수나 표현식의 결과 유형을 컴파일 시간에 추론하는 키워드입니다. 주로 템플릿 프로그래밍에서 복잡한 유형을 추론할 때 주로 사용됩니다.
  • 두 번째 우선순위 큐 선언 방법은 std::funciton을 활용합니다. std::function은 호출 가능한 어떠한 유형의 객체도 래핑 할 수 있는 템플릿 클래스입니다.

 

2. 예제-2

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

int main() {

	auto Predicate = [](const int& a, const int& b) {return a < b; };

	vector<int> v = { 1,5,7,9,3,2,15 };

	sort(begin(v), end(v), Predicate);

	return 0;
}

 

Details

 

  • sort 함수 호출 시 람다 함수로 정의한 함수 객체를 Predicate로 전달할 수 있습니다.