C++에서 sort 함수와 같은 알고리즘을 사용할 때, 비교 함수(comparison function)를 인자로 전달할 수 있다. 비교 함수는 두 개의 인자를 비교하여 그들의 순서를 결정하는 역할을 한다. 예를 들어, std::sort 함수에서는 정렬 기준을 정의하는 비교 함수가 필요하다. 이 비교 함수는 단순히 두 값을 비교하는 함수이지만, 왜 함수가 인자로 전달될 수 있는지에 대한 개념을 이해하기 위해서는 C++의 함수 포인터, 람다 함수, 그리고 함수 객체에 대한 이해가 필요하다.

함수 포인터

C++에서 함수는 특정한 타입을 가진 객체로 취급된다. 함수 포인터는 특정 타입의 함수 주소를 저장할 수 있는 변수로, 이를 통해 함수를 인자로 전달할 수 있다. 함수 포인터는 함수의 주소를 변수에 저장하고, 이를 다른 함수에 인자로 넘겨주어 해당 함수가 다른 함수에서 호출될 수 있게 만든다.

함수 포인터 예시

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

bool compare(int a, int b) {
    return a < b;
}

int main() {
    std::vector<int> vec = {5, 2, 8, 1, 4};
    
    // 함수 포인터를 이용한 sort 호출
    std::sort(vec.begin(), vec.end(), compare);
    
    for (int num : vec) {
        std::cout << num << " ";
    }
    
    return 0;
}

위 코드에서 compare 함수는 std::sort 함수에 비교 함수로 전달된다. compare 함수는 두 인자 ab를 비교하여 a < b일 때 true를 반환한다. 이와 같이 함수의 이름은 함수 포인터로 취급되며, 이를 인자로 넘길 수 있다.

람다 함수

C++11부터는 람다 함수가 도입되었고, 이를 통해 함수를 간결하게 정의하고, 즉석에서 비교 함수와 같은 일시적인 함수 객체를 생성할 수 있다. 람다 함수는 그 자체로 함수 포인터처럼 사용할 수 있다.

람다 함수 예시

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

int main() {
    std::vector<int> vec = {5, 2, 8, 1, 4};
    
    // 람다 함수를 이용한 sort 호출
    std::sort(vec.begin(), vec.end(), [](int a, int b) {
        return a < b;
    });
    
    for (int num : vec) {
        std::cout << num << " ";
    }
    
    return 0;
}

위 코드에서 std::sort에 전달된 람다 함수는 ab를 비교하여 정렬 기준을 정의한다. 람다 함수는 코드 블록 내에서 바로 비교 함수의 역할을 하며, 코드가 더 간결하고 이해하기 쉬운 형태로 작성된다.

함수 객체

C++에서 함수 객체(function object)는 연산자 ()를 오버로드한 클래스로, 객체를 함수처럼 호출할 수 있는 특성을 가진다. 함수 객체는 상태를 가질 수 있기 때문에, 더 복잡한 비교 함수나 사용자 정의 비교 기준을 만들 때 유용하다.

함수 객체 예시

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

class Compare {
public:
    bool operator()(int a, int b) {
        return a < b;
    }
};

int main() {
    std::vector<int> vec = {5, 2, 8, 1, 4};
    
    // 함수 객체를 이용한 sort 호출
    std::sort(vec.begin(), vec.end(), Compare());
    
    for (int num : vec) {
        std::cout << num << " ";
    }
    
    return 0;
}

위 코드에서 Compare 클래스는 operator()를 오버로드하여 함수처럼 동작하는 객체를 만든다. std::sort는 이 함수 객체를 인자로 받아서 정렬을 수행한다. 함수 객체는 상태를 가질 수 있어, 동일한 함수 객체를 여러 번 사용하거나 더 복잡한 로직을 구현할 때 유용하다.

함수 인자로 함수 받는 원리

C++에서 함수는 일종의 객체로 취급되기 때문에, 함수 포인터나 함수 객체, 람다 함수 등은 모두 함수 인자로 전달될 수 있다. 함수 포인터는 함수의 주소를 저장하는 변수이고, 람다 함수는 즉석에서 정의되는 익명 함수 객체이며, 함수 객체는 상태를 가지는 클래스 인스턴스로, 이들 모두가 std::sort와 같은 함수의 인자로 전달되어 기능을 수행한다.

함수 포인터는 특정 타입의 함수 주소를 저장할 수 있는 변수이며, 이를 통해 다른 함수에 함수를 전달할 수 있다. 함수 포인터를 사용하면 호출할 함수의 종류를 실행 중에 동적으로 선택할 수 있어 매우 유용하다. 이번에는 함수 포인터로 함수를 인자로 받는 함수의 구조를 살펴보며, 그 사용 방법에 대해 설명한다.

함수 포인터를 받는 함수

먼저, 함수 포인터를 받는 함수가 어떻게 작동하는지 설명하겠다. 함수 포인터는 함수의 주소를 저장하는 변수이므로, 이를 함수의 매개변수로 전달하면 호출할 함수의 종류를 동적으로 지정할 수 있다.

함수 포인터를 인자로 받는 예시

#include <iostream>

bool compare(int a, int b) {
    return a < b;
}

void sort(int arr[], int size, bool (*cmp)(int, int)) {
    // 비교 함수 포인터를 이용하여 정렬 작업을 한다.
    for (int i = 0; i < size - 1; ++i) {
        for (int j = i + 1; j < size; ++j) {
            if (cmp(arr[i], arr[j])) {
                std::swap(arr[i], arr[j]);
            }
        }
    }
}

int main() {
    int arr[] = {5, 2, 8, 1, 4};
    int size = sizeof(arr) / sizeof(arr[0]);

    // 함수 포인터를 인자로 전달하여 정렬을 수행한다.
    sort(arr, size, compare);

    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }

    return 0;
}

위 코드에서 sort 함수는 cmp라는 함수 포인터를 매개변수로 받는다. cmp는 두 개의 int 값을 받아서 비교하는 함수로, 이 함수 포인터를 사용해 정렬 기준을 정의한다. main 함수에서는 compare 함수를 sort 함수에 전달하여 배열을 정렬한다.

함수 포인터 매개변수

sort 함수에서 사용되는 bool (*cmp)(int, int) 부분은 함수 포인터를 정의하는 문법이다. 여기서 (*cmp)는 함수 포인터를 나타내며, int, int는 비교 함수가 받는 인자의 타입을 나타낸다. 즉, cmp는 두 개의 int 값을 비교하는 함수의 주소를 저장하는 변수이다.

함수 포인터는 그 자체로 변수이기 때문에, sort 함수는 이를 통해 정렬 기준을 동적으로 설정할 수 있다. cmpcompare 함수나 다른 함수로 바꿀 수 있으며, 함수 포인터를 사용하면 유연한 정렬을 할 수 있다.