함수 객체와 람다 표현식

2025. 3. 28. 08:57Programming 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