본문 바로가기

Library/C/C++

STL 벡터 내에서의 원소 정렬

std::vector< T >에 어떤 원소들이 저장되어 있을 경우, T가 기본형이 아니라면 std::sort()와 같은 함수를 사용하기 위해서는 비교 함수를 제공해야 한다. std::sort()를 사용하는 예제는 대부분 기본형을 사용하기 때문에, 실질적으로 컨테이너 안의 원소들을 정렬하고 싶은 경우 난감했던 경우가 있을 것이다.

std::vector< T >의 원소의 정렬을 위한 비교 함수는, 가장 간단하게 다음과 같이 작성할 수 있다.

bool Compare(T& lhs, T& rhs)
{
    return lhs < rhs; // 비교하고 싶은 T 타입의 데이터 멤버를 비교해야 한다
}

이 방법은 비교 함수가 전역으로 선언되어야 하기 때문에 경우에 따라서는 적절하지 않을 수도 있다. 그리고, std::vector< T >을 데이터 멤버로 가지고 있는 클래스 내에서 이것을 멤버 함수로 선언하고 사용하고 싶은 사람도 있겠지만, 클래스 내부의 멤버 함수에 대한 포인터 문제 때문에 가능하지 않다. 따라서, 정렬 함수를 해당 클래스 바깥으로 노출하고 싶지 않거나, 클래스 형태로 포장하고 싶은 경우 STL Functor를 사용하는 것이 좋다. 특히, 같은 원소들에 대해 비교하고 싶다면 단항 함수자를 사용하면 되는데, 다음과 같이 작성하면 함수 포인터를 필요로 하는 호출에 대해서 함수를 호출하는 것처럼 포장할 수 있다.

// 여기서 T는 템플릿을 선언하지 않았다면 실제 데이터 타입을 써줘야 한다
class Compare : std::unary_function< T, bool >
{
public:
    bool operator()(const T& lhs, const T& rhs) const
    {
        return lhs < rhs; // 위와 마찬가지로, 실제 비교하고자 하는 T 타입의 데이터 멤버를 비교한다
    }
};

이제, std::vector< T >를 std::sort()를 사용하여 정렬하고자 하면, 이렇게 만들어진 비교 개체를 비교 함수로 제공하면 된다. 여기서, 함수 호출자에 대한 연산자를 재정의하면서 선언하는 파라미터의 타입은, 해당 컨테이너에 어떤 형식으로 저장되었느냐에 따라 달라진다. 만약, std::vector< T >로 선언된 벡터에 대해 위와 같은 Functor를 제공한다면, const T&는 유효한 선언이다. 하지만, std::vector< T* >로 선언된 벡터에 대해서는 const T*와 같은 형식으로 선언되어야 한다. 여담으로, 모두가 알고 있겠지만 참조에 대한 재참조는 인정되지 않는다. 최근, 표준화 의원회에서 참조에 대한 참조는 원본에 대한 또다른 참조로 처리하자는 의견이 있고, 다음번 표준에는 그것이 반영될 것으로 보인다.

여튼, 이 비교 함수자는 해당 컨테이너의 원소를 정렬하고자 할 때에도 사용할 수 있으며, 이진 탐색과 같은 알고리즘에도 사용할 수 있다.

std::sort(vector.begin(), vector.end(), Compare());
std::binary_search(vector.begin(), vector.end(), T& value, Compare());
// 이진 탐색의 경우, 해당 벡터의 원소들이 순서대로 정렬되어 있지 않으면 함수의 수행 도중 실패한다.


이렇게 만들어진 Compare 비교 개체는 해당 클래스 내에서 적절한 액세스 지정자를 사용하여 공개 범위를 적절하게 통제할 수도 있다. 전역으로 선언되는 함수는 네임 스페이스만으로 그 범위를 통제해야 하는 것에 비하면, 조금 더 나은 장점이라고 할 수 있다.