함수 객체와 람다 표현식
2025. 3. 28. 08:57ㆍProgramming Languages/C++
8.5 함수 객체와 람다 표현식
8.5.1 함수 객체(Functors)
함수처럼 동작하는 객체입니다:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 함수 객체
// 사용자 정의 함수 객체
class Multiplier {
private:
int factor;
public:
Multiplier(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
// 사용자 정의 비교 함수 객체
class CompareBySecond {
public:
template <typename T>
bool operator()(const T& a, const T& b) const {
return a.second < b.second;
}
};
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 함수 객체 사용
Multiplier mul3(3);
std::cout << "3 * 4 = " << mul3(4) << std::endl;
// transform 알고리즘과 함께 사용
std::vector<int> result(vec.size());
std::transform(vec.begin(), vec.end(), result.begin(), Multiplier(2));
std::cout << "결과 (각 요소 * 2): ";
for (const auto& elem : result) {
std::cout << elem << " ";
}
std::cout << std::endl;
// 표준 함수 객체
// 산술 연산
std::plus<int> add;
std::minus<int> subtract;
std::multiplies<int> multiply;
std::divides<int> divide;
std::modulus<int> modulo;
std::negate<int> negate;
std::cout << "5 + 3 = " << add(5, 3) << std::endl;
std::cout << "5 - 3 = " << subtract(5, 3) << std::endl;
std::cout << "5 * 3 = " << multiply(5, 3) << std::endl;
std::cout << "5 / 3 = " << divide(5, 3) << std::endl;
std::cout << "5 % 3 = " << modulo(5, 3) << std::endl;
std::cout << "-5 = " << negate(5) << std::endl;
// 비교 연산
std::equal_to<int> eq;
std::not_equal_to<int> neq;
std::greater<int> gt;
std::less<int> lt;
std::greater_equal<int> gte;
std::less_equal<int> lte;
std::cout << "5 == 3: " << eq(5, 3) << std::endl;
std::cout << "5 != 3: " << neq(5, 3) << std::endl;
std::cout << "5 > 3: " << gt(5, 3) << std::endl;
std::cout << "5 < 3: " << lt(5, 3) << std::endl;
std::cout << "5 >= 3: " << gte(5, 3) << std::endl;
std::cout << "5 <= 3: " << lte(5, 3) << std::endl;
// 논리 연산
std::logical_and<bool> land;
std::logical_or<bool> lor;
std::logical_not<bool> lnot;
std::cout << "true && false: " << land(true, false) << std::endl;
std::cout << "true || false: " << lor(true, false) << std::endl;
std::cout << "!true: " << lnot(true) << std::endl;
// 사용자 정의 비교 함수 객체 사용
std::vector<std::pair<int, int>> pairs = {{1, 5}, {2, 3}, {3, 8}, {4, 1}};
std::sort(pairs.begin(), pairs.end(), CompareBySecond());
std::cout << "pairs (second 기준 정렬): ";
for (const auto& p : pairs) {
std::cout << "(" << p.first << ", " << p.second << ") ";
}
std::cout << std::endl;
return 0;
}
8.5.2 람다 표현식(Lambda Expressions) (C++11)
익명 함수를 정의하는 간결한 방법입니다:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // std::function
int main() {
std::vector<int> vec = {5, 7, 3, 1, 9, 2, 8, 4, 6};
// 기본 람다 표현식 (매개변수와 반환 타입 모두 추론됨)
auto printElem = [](int n) {
std::cout << n << " ";
};
std::cout << "모든 요소: ";
std::for_each(vec.begin(), vec.end(), printElem);
std::cout << std::endl;
// 캡처(capture) 사용
int threshold = 5;
// 값으로 캡처 (by value)
auto countAbove = [threshold](const std::vector<int>& v) {
return std::count_if(v.begin(), v.end(),
[threshold](int n) { return n > threshold; });
};
std::cout << threshold << "보다 큰 요소 개수: " << countAbove(vec) << std::endl;
// 참조로 캡처 (by reference)
int sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int n) {
sum += n;
});
std::cout << "합계 (참조 캡처 사용): " << sum << std::endl;
// 모든 변수 캡처
int factor = 2;
int offset = 3;
// 모든 변수를 값으로 캡처
auto transformVal = [=](int n) { return n * factor + offset; };
// 모든 변수를 참조로 캡처
auto transformRef = [&](int n) { return n * factor + offset; };
std::cout << "변환 결과 (값 캡처): " << transformVal(10) << std::endl;
// 명시적 반환 타입
auto divide = [](double a, double b) -> double {
if (b == 0.0) return 0.0; // 0으로 나누기 방지
return a / b;
};
std::cout << "나눗셈 결과: " << divide(10.0, 2.5) << std::endl;
// 제네릭 람다 (C++14)
auto genericMin = [](auto a, auto b) {
return (a < b) ? a : b;
};
std::cout << "최소값 (int): " << genericMin(5, 10) << std::endl;
std::cout << "최소값 (double): " << genericMin(3.14, 2.71) << std::endl;
std::cout << "최소값 (string): " << genericMin(std::string("abc"), std::string("def")) << std::endl;
// 람다를 함수 객체로 저장
std::function<bool(int)> isEven = [](int n) { return n % 2 == 0; };
std::function<bool(int)> isOdd = [](int n) { return n % 2 != 0; };
std::cout << "짝수의 개수: " << std::count_if(vec.begin(), vec.end(), isEven) << std::endl;
std::cout << "홀수의 개수: " << std::count_if(vec.begin(), vec.end(), isOdd) << std::endl;
// 람다를 사용한 정렬
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return std::abs(a) < std::abs(b); // 절대값으로 정렬
});
std::cout << "절대값 기준 정렬: ";
for (const auto& elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;
// 재귀 람다 (C++14, auto 반환 타입 사용)
std::function<int(int)> factorial = [&factorial](int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
};
std::cout << "5 팩토리얼: " << factorial(5) << std::endl;
return 0;
}
8.5.3 바인더(Binders)와 함수 어댑터
함수의 인자를 부분적으로 고정하거나 변환하는 기능을 제공합니다:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 바인더
// 세 개의 인자를 받는 함수
int compute(int a, int b, int c) {
return a * b + c;
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// std::bind 사용 (C++11)
// compute 함수의 첫 번째 인자를 2로, 세 번째 인자를 10으로 고정
auto boundCompute = std::bind(compute, 2, std::placeholders::_1, 10);
// boundCompute(3)은 compute(2, 3, 10)과 같음
std::cout << "compute(2, 3, 10): " << boundCompute(3) << std::endl;
// 인자 순서 변경
auto swappedCompute = std::bind(compute, std::placeholders::_2, std::placeholders::_1, std::placeholders::_3);
// swappedCompute(5, 2, 10)은 compute(2, 5, 10)과 같음
std::cout << "compute(2, 5, 10): " << swappedCompute(5, 2, 10) << std::endl;
// std::bind와 멤버 함수
struct Point {
int x, y;
Point(int x, int y) : x(x), y(y) {}
bool isInRange(int minX, int minY, int maxX, int maxY) const {
return x >= minX && x <= maxX && y >= minY && y <= maxY;
}
};
std::vector<Point> points = {
{3, 5}, {8, 2}, {1, 9}, {5, 7}, {2, 4}
};
// 특정 범위 내의 점 찾기
auto inRectangle = std::bind(&Point::isInRange, std::placeholders::_1, 2, 3, 6, 8);
// 범위 내의 점 개수 세기
int count = std::count_if(points.begin(), points.end(), inRectangle);
std::cout << "범위 내의 점 개수: " << count << std::endl;
// 람다로 동일한 작업 수행 (종종 더 읽기 쉬움)
count = std::count_if(points.begin(), points.end(), [](const Point& p) {
return p.isInRange(2, 3, 6, 8);
});
std::cout << "범위 내의 점 개수 (람다 사용): " << count << std::endl;
// std::not_fn (C++17)
// 주어진 함수의 결과를 반전시킴
auto isEven = [](int n) { return n % 2 == 0; };
auto isOdd = std::not_fn(isEven);
std::cout << "5는 홀수인가? " << std::boolalpha << isOdd(5) << std::endl;
// bind와 std::mem_fn 사용
// 멤버 함수를 함수 객체로 변환
auto getX = std::mem_fn(&Point::x);
// 모든 점의 x 좌표 출력
std::cout << "모든 점의 x 좌표: ";
for (const auto& p : points) {
std::cout << getX(p) << " ";
}
std::cout << std::endl;
return 0;
}
'Programming Languages > C++' 카테고리의 다른 글
고급 주제 (0) | 2025.03.28 |
---|---|
챕터8. 실습 문제 (0) | 2025.03.28 |
STL 알고리즘 (0) | 2025.03.28 |
STL 반복자 (0) | 2025.03.28 |
STL 컨테이너 (0) | 2025.03.28 |