set与重载<

set是有序容器,在定义容器的时候必须要指定 key 的比较函数。只不过这个函数通常是默认的 less,表示小于关系,不用特意写出来:

template<

class Key, // 模板参数是key类型,即元素类型

class Compare = std::less // 比较函数

> 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

#include

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 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<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_unique_pos(const key_type&) [with _Ke

y = Point; _Val = Point; _KeyOfValue = std::_Identity; _Compare = std::less; _Alloc = std::allocator; 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, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_emplace_unique(_Args&& ...) [with _Args = {Point}; _Key = Point;

_Val = Point; _KeyOfValue = std::_Identity; _Compare = std::less; _Alloc = std::allocator]’

/usr/include/c++/9/bits/stl_set.h:463:64: required from ‘std::pair, _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; _Alloc = std::allocator; 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]’

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&)’

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 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 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 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 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

#include

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 s;

s.emplace(Point(1.1, 2.2));

s.emplace(Point(2.2, 3.3));

return 0;

}