스토리텔링 개발자

[Effective STL] 42. less<T> 본문

개발/Effective STL

[Effective STL] 42. less<T>

김디트 2025. 1. 10. 10:56
728x90

항목 42. less<T>는 operator<의 의미임을 꼭 알아두자

 

 

 

less<T>의 기본 동작
  • 아래처럼 기본적으로 무게로 정렬되는 Widget이 있다.
class Widget
{
public:
    ...
    size_t weight() const;
    size_t maxSpeed() const;
    
    // 무게로 정렬되는 operator<
    bool operator<(const Widget& lhs, const Widget& rhs)
    {
        return lhs.weight() < rhs.weight();
    }
};
  • 헌데, 최대 속도(maxSpeed)를 가지고 정렬하는 multiset<Widget>을 만들고 싶다면?
  • multiset<Widget>의 기본 비교 함수는 less<Widget>이다.
  • 하지만, less<Widget>은 기본적으로 Widget 객체 두 개에 대해 operator<를 호출하여 동작한다.

 

 

문제 해결하기(잘못된 예)
  • 템플릿 특수화로 less<Widget>과 operator<의 관계를 끊어버린다.
// 템플릿 특수화로 처리한다.
template<>
struct std::less<Widget>
{
    bool operator()(const Widget& lhs, const Widget& rhs) const
    {
        return lhs.maxSpeed() < rhs.maxSpeed();
    }
};
  • 일반적으로 std 내의 STL 컴포넌트를 수정하는 일은 금지되어 있다.
    • 하지만 일부 경우에는 약간의 처리(tinkering)은 허용된다.
    • 사용자 정의 타입에 대해서는 std 템플릿을 조정해서 한정할 수 있다.
    • 그래서 위 코드는 컴파일 에러가 발생하지 않는다.
    • STL 컴포넌트를 수정해야 하는 상황 예시(C++98 한정)
    • // shared_ptr이 표준이 아니었을 때는 이런 처리가 필요했다.
      namespace std
      {
          tempalte<typename T>
          struct less<shared_ptr<T>>
          {
              bool operator()(const shared_ptr<T>& a, const shared_ptr<T>& b) const
              {
                  // 단지 less에 T*가 들어왔을 때처럼 동작하게 래핑했을 뿐이다.
                  return less<T*>()(a.get(), b.get());
              }
          }
      }
  • 기본적으로 사용자는 less가 operator<와 동일한 동작을 하는 것으로 기대한다.
    • 하지만 템플릿 특수화 버전은 그걸 가볍게 무시해버리며, 직관성이 무너진다.
multiset<Widget> widgets; // 누가 봐도 operator<로 정렬될 것 같지만....
// 템플릿 특수화 버전의 less로 정렬됨을 추론할 수가 없다.

 

 

 

직관적으로 문제 해결하기
  • less 이외의 함수 객체로 비교를 수행하게 한다.
struct MaxSpeedCompare
{
    bool operator()(const Widget& lhs, const Widget& rhs) const
    {
        return lhs.maxSpeed() < rhs.maxSpeed();
    }
};

multiset<Widget, MaxSpeedCompare> widgets; // 비교를 수행할 함수 객체를 사용하여 선언
  • 람다를 사용할 수도 있다.
// 물론 람다를 사용할 수도 있다.
auto compare = [](const Widget& lhs, const Widget& rhs)
{
    return lhs.maxSpeed() < rhs.maxSpeed();
};

multiset<Widget, decltype(compare)> widgets(compare); // 인자로 람다를 넘겨줘야 한다.

 

728x90
Comments