C++踩坑--set与重载<
set与重载<
set是有序容器,在定义容器的时候必须要指定 key 的比较函数。只不过这个函数通常是默认的 less,表示小于关系,不用特意写出来:
template<
class Key, // 模板参数是key类型,即元素类型
class Compare = std::less<Key> // 比较函数
> class set; // 集合
C++ 里的 int、string 等基本类型都支持比较排序,放进有序容器里毫无问题。但很多自定义类型没有默认的比较函数,要作为容器的 key 就有点麻烦。虽然这种情况不多见,但有的时候还真是个“刚性需求”。解决这个问题一般是重载“<”,比如说我们有一个 Point 类,它是没有大小概念的,但只要给它重载“<”操作符,就可以放进有序容器里了:
/*************************************************************************
> File Name: 03-set.cpp
> Author:
> Mail:
> Created Time: Mon 25 Sep 2023 05:04:02 PM CST
************************************************************************/
#include <set>
#include <iostream>
using namespace std;
class Point
{
public:
Point(double x, double y) : m_x(x), m_y(y) {}
~Point() {}
bool operator < (const Point& rhs)
{
return m_x > rhs.m_x;
}
private:
double m_x, m_y;
};
int main()
{
set<Point> s;
s.emplace(Point(1.1, 2.2));
s.emplace(Point(2.2, 3.3));
return 0;
}
由于我是一个新手C++使用者,上述代码尽管重载了小于(<)运算符,但是编译报错:
ydqun@ydqhost chapter12 % g++ 03-set.cpp [0]
In file included from /usr/include/c++/9/bits/stl_tree.h:65,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Point]’:
/usr/include/c++/9/bits/stl_tree.h:2095:11: required from ‘std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Ke
y = Point; _Val = Point; _KeyOfValue = std::_Identity<Point>; _Compare = std::less<Point>; _Alloc = std::allocator<Point>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = Point]’
/usr/include/c++/9/bits/stl_tree.h:2413:19: required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_emplace_unique(_Args&& ...) [with _Args = {Point}; _Key = Point;
_Val = Point; _KeyOfValue = std::_Identity<Point>; _Compare = std::less<Point>; _Alloc = std::allocator<Point>]’
/usr/include/c++/9/bits/stl_set.h:463:64: required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Tp>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<
_Key, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {Point}; _Key = Point; _Compare = std::less<Point>; _Alloc = std::allocator<Point>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Tp>, _Compare, typename __gnu_cxx::__
alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<Point>]’
03-set.cpp:28:30: required from here
/usr/include/c++/9/bits/stl_function.h:386:20: error: no match for ‘operator<’ (operand types are ‘const Point’ and ‘const Point’)
386 | { return __x < __y; }
| ~~~~^~~~~
03-set.cpp:16:10: note: candidate: ‘bool Point::operator<(const Point&)’ <near match>
16 | bool operator < (const Point& rhs)
| ^~~~~~~~
03-set.cpp:16:10: note: passing ‘const Point*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c++/9/bits/stl_algobase.h:64,
from /usr/include/c++/9/bits/stl_tree.h:63,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_pair.h:454:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator<(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)’
454 | operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
| ^~~~~~~~
/usr/include/c++/9/bits/stl_pair.h:454:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/9/bits/stl_tree.h:65,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_function.h:386:20: note: ‘const Point’ is not derived from ‘const std::pair<_T1, _T2>’
386 | { return __x < __y; }
| ~~~~^~~~~
In file included from /usr/include/c++/9/bits/stl_algobase.h:67,
from /usr/include/c++/9/bits/stl_tree.h:63,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_iterator.h:331:5: note: candidate: ‘template<class _Iterator> bool std::operator<(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_Iterator>&)’
...... 中间省略若几十行(无力吐槽C++编译出错提示)
/usr/include/c++/9/bits/stl_function.h:386:20: note: ‘const Point’ is not derived from ‘const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>’
386 | { return __x < __y; }
| ~~~~^~~~~
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/bits/locale_classes.h:40,
from /usr/include/c++/9/bits/ios_base.h:41,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from 03-set.cpp:8:
/usr/include/c++/9/bits/basic_string.h:6239:5: note: candidate: ‘template<class _CharT, class _Traits, class _Alloc> bool std::operator<(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&, const _CharT*)’
6239 | operator<(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:6239:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/9/bits/stl_tree.h:65,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_function.h:386:20: note: ‘const Point’ is not derived from ‘const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>’
386 | { return __x < __y; }
| ~~~~^~~~~
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/bits/locale_classes.h:40,
from /usr/include/c++/9/bits/ios_base.h:41,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from 03-set.cpp:8:
/usr/include/c++/9/bits/basic_string.h:6251:5: note: candidate: ‘template<class _CharT, class _Traits, class _Alloc> bool std::operator<(const _CharT*, const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&)’
6251 | operator<(const _CharT* __lhs,
| ^~~~~~~~
/usr/include/c++/9/bits/basic_string.h:6251:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/9/bits/stl_tree.h:65,
from /usr/include/c++/9/set:60,
from 03-set.cpp:7:
/usr/include/c++/9/bits/stl_function.h:386:20: note: mismatched types ‘const _CharT*’ and ‘Point’
386 | { return __x < __y; }
| ~~~~^~~~~
In file included from /usr/include/c++/9/bits/ios_base.h:46,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from 03-set.cpp:8:
/usr/include/c++/9/system_error:208:3: note: candidate: ‘bool std::operator<(const std::error_code&, const std::error_code&)’
208 | operator<(const error_code& __lhs, const error_code& __rhs) noexcept
| ^~~~~~~~
/usr/include/c++/9/system_error:208:31: note: no known conversion for argument 1 from ‘const Point’ to ‘const std::error_code&’
208 | operator<(const error_code& __lhs, const error_code& __rhs) noexcept
| ~~~~~~~~~~~~~~~~~~^~~~~
/usr/include/c++/9/system_error:282:3: note: candidate: ‘bool std::operator<(const std::error_condition&, const std::error_condition&)’
282 | operator<(const error_condition& __lhs,
| ^~~~~~~~
/usr/include/c++/9/system_error:282:36: note: no known conversion for argument 1 from ‘const Point’ to ‘const std::error_condition&’
282 | operator<(const error_condition& __lhs,
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~
相信不少新入坑C++的同学看到这类报错后头都大了(我也是)。
经过我长达半个小时的排查,终于从出错提示中找到答案,在报错信息中的第四行,如下:
/usr/include/c++/9/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Point]’:
这时候,我们可以知道是因为在类内重载<运算符时,少了const修饰,导致函数签名不匹配,我们只需要对重载的operator<()函数后面加上const修饰,即可解决,完整代码如下:
/*************************************************************************
> File Name: 03-set.cpp
> Author:
> Mail:
> Created Time: Mon 25 Sep 2023 05:04:02 PM CST
************************************************************************/
#include <set>
#include <iostream>
using namespace std;
class Point
{
public:
Point(double x, double y) : m_x(x), m_y(y) {}
~Point() {}
bool operator < (const Point& rhs) const
{
return m_x > rhs.m_x;
}
private:
double m_x, m_y;
};
int main()
{
set<Point> s;
s.emplace(Point(1.1, 2.2));
s.emplace(Point(2.2, 3.3));
return 0;
}