C++ STL에서 제공하는 set, map, multiset, multimap 등의 컨테이너는 자동으로 데이터를 정렬된 상태로 유지시켜 줍니다. 그래서 이러한 컨테이너에 데이터를 넣고 그대로 출력하기만 하면 오름차순으로 정렬된 데이터를 얻을 수 있죠. 하지만 가끔은 내림차순 정렬이 필요하거나 내가 원하는 방식대로 정렬되어야 하는 경우도 생깁니다. 이럴 때 어떻게 할 수 있을까요? set 컨테이너로 예를 들어 설명해 보겠습니다.
set의 템플릿(template)을 살펴보면 다음과 같이 되어 있습니다.
template <class _Kty, class _Pr = less<_Kty>, class _Alloc = allocator<_Kty>>
_Kty 부분은 흔히 사용하는 키 타입을 설정하는 부분입니다. 우리가 봐야 할 부분은 바로 _Pr 부분입니다. 기본 값으로 less가 설정된 모습을 볼 수 있는데 이 less는 아래와 같이 정의되어 있습니다.
template <class _Ty = void>
struct less {
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;
_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type;
constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
return _Left < _Right;
}
};
less는 구조체 형태로 정의되어 있고 내부에는 operator() 형태로 연산자 오버로딩(operator overloading)이 사용되고 있습니다. 또한 그 내용을 보면 _Left와 _Right의 크기를 비교해서 결과를 반환하고 있죠. 바로 이 less가 기본적으로 설정되어 있었기 때문에 오름차순으로 정렬되었던 것입니다.
때문에 정렬 기준을 바꾸기 위해선 저 _Pr 부분에 다른 코드를 넣어주면 됩니다. 단순 비교만 하는 경우 STL 자체에서 지원하는 greater, less, greater_equal, less_equal 등이 있으므로 골라서 사용하시면 됩니다. 만약 내림차순으로 정렬 기준을 바꾸고 싶은 경우 greater를 이용해 코딩하시면 됩니다.
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<string, greater<string>> s;
s.insert("apple");
s.insert("banana");
s.insert("melon");
for (const auto& str : s) {
cout << str << endl;
}
return 0;
}
정렬 기준을 내가 원하는 대로 직접 만들어야 하는 경우도 있습니다. 그럴 땐 직접 비교용 구조체를 만들어서 사용하시면 됩니다. 예를 들어 오름차순 정렬이지만 길이가 짧은 문자열을 우선해서 정렬해야 하는 경우를 생각해 보겠습니다. 그럴 경우 아래와 같이 코딩하시면 됩니다.
#include <iostream>
#include <set>
using namespace std;
// 직접 추가한 비교용 구조체
struct myOrder
{
bool operator() (const string& left, const string& right) const
{
if (left.size() == right.size()) {
return left < right;
}
else {
return left.size() < right.size();
}
}
};
int main()
{
set<string, myOrder> s;
s.insert("apple");
s.insert("banana");
s.insert("melon");
for (const auto& str : s) {
cout << str << endl;
}
return 0;
}
지금까지 set 컨테이너로 예를 들어 설명했지만 다른 컨테이너 역시 같은 방법으로 적용시켜서 사용하시면 됩니다.