본문 바로가기
컴퓨터/디버깅

no match for ‘operator<’ in ‘__x < __y’

by adnoctum 2010. 4. 15.
위와 같은 에러는 주로 std::map이나 std::set에 넣는 객체가 operator< 를 갖고 있지 않을 때 발생한다. 전체 에러 메세지는 다음과 같다. (보면 알듯이 gcc 에서 난 에러 메세지임, VC++ 도 비슷한 류의 메세지가 뿌려질 것이다)

g++ -c merge_norm_pcc_rank_networks.cpp
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = edge]’:
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:347:   instantiated from ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = edge, _Tp = double, _Compare = std::less<edge>, _Alloc = std::allocator<std::pair<const edge, double> >]’
merge_norm_pcc_rank_networks.cpp:133:   instantiated from here
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_function.h:227: error: no match for ‘operator<’ in ‘__x < __y’
make: *** [merge_norm_pcc_rank_networks.o] Error 1


눈여겨 보아야 할 곳은 위의 푸른색 부분. 내용은 merge_norm_pcc_rank_networks.cpp 133번째 줄에서 stl_function.h 파일의 227번째 줄을 호출하면서, 여기서 다시 operator< 가 호출되었는데, 그것이 없다는 것이다. 컴파일러가 에러가 났다고 하는 merge_norm_pcc_rank_networks.cpp 파일의 133번째 줄은 다음과 같다.

std::vector<std::string> v;
// 어떤 작업
std::map<edge,double> edge2rank;
edge e(v[2],v[3]);
edge2rank[e] = atof(v[0].c_str()); // 여기가 133번째 줄


언뜻 보면 왜 에러가 났는지 명확하지는 않다. 그러나, STL에서 set과 map의 일반적 구현에 대해 생각해 보면 쉽게 생각할 수 있다.

즉, set과 map은 요소를 찾아 가는 속도를 높이기 위해 일반적으로 binary search를 이용하도록 구현되었으며, 따라서 set과 map 에 넣는 개체는 그 개체에서 operator< 를 제공해 주어야만 binary search 를 사용할 수 있다. 즉, 만약 두 개체의 대소 관계를 비교할 수 없다면 binary search를 할 수 없고, 그러면 set이나 map 에 그 데이터를 넣을 수 없게 된다.

위에서 에러가 난 133 번 줄은 실제로, edge2rank.operator[](const edge& e) 와 같은 호출을 할테고(실제로 이 부분이 에러 메세지에 있다, 이와 같은 호출을 하는 과정에서 위의 문제에 부딪혀 에러가 났을 테니까),  이 때 e 의 적절한 위치를 결정하기 위해 edge2rank 에 있는 다른 개체들과 비교를 하기 위해 edge 개체의 opeator< 를 호출하려고 했을 것이다. 그런데 그게 없으니 에러가 난 것이다. 해결 방법은 edge class에 operator< 를 만들어 주면 된다. 예를 들면 다음과 같이.

bool edge::operator<(const edge& e) const {
    return (_node1 > _node2 ? _node1 : _node2) > (e._node1 > e._node2 ? e._node1 : e._node2);
}
(주의: 위의 operator< 의 정의는 좋지 않다. 실제로 내가 원하는 기능을 하지 못했다. operator< 이 제대로 정의되었는지 알아 보는 것은 다음 기회에 살펴 본다)

그러면 map인 edge2rank 에 e 를 넣기 위해, edge2rank 에 있는 개체 i 에 대하여 e.operator<(i)를 호출하면서 e 가 들어갈 적절한 위치를 판단할 것이다. 위의 예에서 edge는 std::string 으로 된 노드 두 개만 갖는 개체로, 두 개체의 순서에 상관없이 두 노드가 같기만 하면 같은 개체를 나타내고자 한 클래스이다. 따라서 두 개의 노드 중 큰 노드가 크면 작으면 된다.

또는, edge type을 비교할 수 있는 binary predicator를 set 이나 map 에 넘겨줄 수도 있다. 개인적으로 이런 경우는 별로 없었지만.

또한 위의 어떠한 방법을 사용하든 binary predicator는 strict weak ordering 이어야 한다. 즉, 두 개체 a와 b 에 대하여 a < b 와 b < a 가 동시에 참이 될 수 없어야 한다. 만약 그렇지 않다면 binary search 가 제대로 동작할 것이라고 장담할 수 없기 때문이다.