实现自定义类型比较的三种改写方法 -- 以set<Point>为例

前置

模板 – 打印

CppBaseDay21

重载 <<

os 是泛化的 std::ostream

  • operator<< 函数中,osstd::ostream 的引用,这意味着它可以代表任何具体的输出流,例如 std::coutstd::ofstream(文件输出流)、std::ostringstream(字符串输出流)等。
  • 当你向 operator<< 传递 std::cout 时,os 实际上就是 std::cout 的引用;但你也可以将其他流传递给 os,例如文件流对象或字符串流对象。

cout 是特定的 std::ostream 实例

  • std::cout 是一个具体的 std::ostream 实例,它与标准输出(通常是终端/控制台)相关联。你可以直接用 std::cout 来向屏幕打印消息。
  • cout 不能替代 os,因为 os 可以是任何输出流,而 cout 仅指向标准输出。

使用namespace std { }的原因

当我们对标准库模板进行特化时,必须将特化放入 std 命名空间,因为这些模板是在 std 命名空间中定义的。如果不放在 std 命名空间中,编译器将无法将其视为对标准模板的特化,而是会当成一个独立的新模板。


第一种方式 – 模板的特化

类模板特化 template<> struct less<Point>

//类模板定义
template <class/typename T, ...>
class 类名{
//类定义......
};
//chatgpt给出的示例:
namespace std {
    template <class T>	//less<Point> 是对模板 less<T> 的特化,Point 是 T 的具体类型
    struct less {
       ……
    };
}

//代码中的写法 
template <>
struct less<Point>//模板的特化
{
	……
}
  • template<>:这是模板特化的语法,表示我们将为模板类 std::less 提供一个特化版本,用于处理 Point 类型。

  • struct less<Point>:我们在 std::less 模板中为 Point 类型提供特化,即定义一个新的规则用于比较 Point 对象。。

这个特化会覆盖默认的 std::less 行为–在标准库中,std::less 是一个类模板,用于定义对象之间的比较操作(通常是 < 运算符)。它的默认实现会使用 < 运算符来比较对象。

struct less<Point> 是对 std::less 这个模板类的特化,这里的 Point 具体化了模板参数 T

less 是类名,T 是一个类型参数。这个模板类可以用来创建不同类型的比较器,例如 less<int>less<double> 等。

  • operator():这是重载的函数调用运算符,定义了 less<Point> 如何比较两个 Point 对象。在这个例子中,我们首先比较 x 坐标,如果 x 坐标相等,则比较 y 坐标。

为什么要末尾要加const–成为const成员函数被调用

const/非const对象 以及 const/非const函数

在 const 对象上调用成员函数

  • 只能调用 const 成员函数。
  • 不能调用非常量成员函数。

在非常量对象上调用成员函数

  • 可以调用 const 和非 const 成员函数(优先,没有再调用const版本)。
chatgpt例子
class MyClass {
public:
    int value;

    // 非 const 成员函数
    void setValue(int v) {
        value = v; // 修改值
    }

    // const 成员函数
    int getValue() const {
        return value; // 只读
    }
};

int main() {
    MyClass obj; // 非 const 对象
    obj.setValue(5); // 可以调用
    int val = obj.getValue(); // 可以调用

    const MyClass constObj; // const 对象
    // constObj.setValue(10); // 错误,不能调用非 const 成员函数
    int constVal = constObj.getValue(); // 可以调用 const 成员函数

    return 0;
}
set源码中的成员函数operator()

看看吧。。。主要看图,格式就是这样要求的。。。

STL 容器(如 std::setstd::map)通常会使用传递的比较函数来比较元素。为了保证这些比较函数能够在使用时不改变容器中元素的状态,STL 要求这些函数必须是 可调用的(invocable)并且应该是 const 的。

以下是个人猜想。。。

set底层是const修饰的,因此set的对象只能调用const 成员函数

所以bool operator()(const Point &lhs,const Point &rhs)后面要加 const

//error
bool operator()(const Point &lhs,const Point &rhs) {……}
bool operator()(const Point &lhs,const Point &rhs) const {……}

在这里插入图片描述

//默认的模板
#if 0
namespace std
{
template <class T>
struct less
{
    bool operator()(const T &lhs, const T &rhs) const
    {
        return lhs < rhs;
    }
};
}//end of namespace std
#endif

形式

template <>
struct less<Point>//模板的特化
{
	……
}

代码1

在这里插入图片描述

#include <math.h>
#include <iostream>
#include <set>

using std::cout;
using std::endl;
using std::set;

//模板 -- 打印
template <typename Container>    //typename - class 
void display(const Container & con){
    for(auto & elem : con){
        cout << elem << " ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {
    }

    //计算点到原点的距离
    float getDistance() const
    {
        return hypot(_ix, _iy);
    }

    int getX() const
    {
        return _ix;
    }

    int getY() const
    {
        return _iy;
    }

    ~Point()
    {
    }

    friend std::ostream & operator<<(std::ostream & os,const Point & rhs);   //Point

private:
    int _ix;
    int _iy;
};

//由于是自定义模板,需要重载 <<
//同时由于需要访问Point类的私有成员,因此在Point类中需要把该函数声明为友元
std::ostream & operator<<(std::ostream & os,const Point & rhs){
    os << "(" << rhs._ix << "," << rhs._iy << ")";
    return os;
}


//第一种 -- 模板的特化
namespace std
{
template <>
struct less<Point>  //less是类名,T是一个类型参数。例如 less<int>、less<double> 等。
{
    //比较2个Point类对象
    bool operator()(const Point &lhs,const Point &rhs) const
    {   
        cout << "template<> struct less" << endl;
        // x 和 y均采用升序的方式比较大小
        if(lhs.getX() < rhs.getX())
        {
            return true;
        }else if(lhs.getX() == rhs.getX()){
            if(lhs.getY() < rhs.getY()){
                return true;
            }else{
                return false;
            }
        }
        else    //lhs.getX() > rhs.getX()
        {
            return false;
        }
    } 
};
}   //end of namespace std


//第二种 -- 运算符重载


//第三种 -- 函数对象



void test0(){
    set<Point> number = {
        Point(1, 2),
        Point(-1, 2),
        Point(1, 4),
        Point(1, 2),
        Point(1, -2),
        Point(3, 2),
    };

    display(number);
}

int main()
{
    test0();       
    return 0;
}  

第二种方式 – 函数对象

形式

struct ComparePoint
{
	……
}

代码2

在这里插入图片描述

#include <math.h>
#include <iostream>
#include <set>

using std::cout;
using std::endl;
using std::set;

//模板 -- 打印
template <typename Container>    //typename - class 
void display(const Container & con){
    for(auto & elem : con){
        cout << elem << " ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {
    }

    //计算点到原点的距离
    float getDistance() const
    {
        return hypot(_ix, _iy);
    }

    int getX() const
    {
        return _ix;
    }

    int getY() const
    {
        return _iy;
    }

    ~Point()
    {
    }

    friend std::ostream & operator<<(std::ostream & os,const Point & rhs);   //Point
    
    //friend struct ComparePoint;	//好像没将函数对象作为友元,代码运行也可以成功。。。
    								//因为使用了getX() 之类的函数,可以直接访问私有成员

private:
    int _ix;
    int _iy;
};

//由于是自定义模板,需要重载 <<
//同时由于需要访问Point类的私有成员,因此在Point类中需要把该函数声明为友元
std::ostream & operator<<(std::ostream & os,const Point & rhs){
    os << "(" << rhs._ix << "," << rhs._iy << ")";
    return os;
}

//第一种 -- 模板的特化

//第二种 -- 函数对象
struct ComparePoint
{
    //比较2个Point类对象
    bool operator()(const Point &lhs,const Point &rhs) const
    {   
        cout << "struct ComparePoint" << endl;
        // x 和 y均采用升序的方式比较大小
        if(lhs.getX() < rhs.getX())
        {
            return true;
        }else if(lhs.getX() == rhs.getX()){
            if(lhs.getY() < rhs.getY()){
                return true;
            }else{
                return false;
            }
        }
        else    //lhs.getX() > rhs.getX()
        {
            return false;
        }
    } 
};

//第三种 -- 运算符重载



void test0(){
    set<Point,ComparePoint> number = {
        Point(1, 2),
        Point(-1, 2),
        Point(1, 4),
        Point(1, 2),
        Point(1, -2),
        Point(3, 2),
    };

    display(number);
}

int main()
{
    test0();       
    return 0;
}  

第三种方式 – 运算符重载

形式

bool operator<(const Point & lhs,const Point & rhs)
{
	……
}

代码

在这里插入图片描述

#include <math.h>
#include <iostream>
#include <set>

using std::cout;
using std::endl;
using std::set;

//模板 -- 打印
template <typename Container>    //typename - class 
void display(const Container & con){
    for(auto & elem : con){
        cout << elem << " ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {
    }

    //计算点到原点的距离
    float getDistance() const
    {
        return hypot(_ix, _iy);
    }

    int getX() const
    {
        return _ix;
    }

    int getY() const
    {
        return _iy;
    }

    ~Point()
    {
    }

    friend std::ostream & operator<<(std::ostream & os,const Point & rhs);   //Point

    friend bool operator<(const Point &lhs, const Point &rhs);  // < 运算符重载

private:
    int _ix;
    int _iy;
};

//由于是自定义模板,需要重载 <<
//同时由于需要访问Point类的私有成员,因此在Point类中需要把该函数声明为友元
std::ostream & operator<<(std::ostream & os,const Point & rhs){
    os << "(" << rhs._ix << "," << rhs._iy << ")";
    return os;
}

//第一种 -- 模板的特化

//第二种 -- 函数对象

//第三种 -- 运算符重载
bool operator<(const Point & lhs,const Point & rhs){
    cout << "bool operator<" << endl;
    if(lhs.getX() < rhs.getX())
        {
            return true;
        }else if(lhs.getX() == rhs.getX()){
            if(lhs.getY() < rhs.getY()){
                return true;
            }else{
                return false;
            }
        }
        else    //lhs.getX() > rhs.getX()
        {
            return false;
        }
}



void test0(){
    set<Point> number = {
        Point(1, 2),
        Point(-1, 2),
        Point(1, 4),
        Point(1, 2),
        Point(1, -2),
        Point(3, 2),
    };

    display(number);
}

int main()
{
    test0();       
    return 0;
}  

补充

如果模板的特化运算符重载同时存在

优先调用 模板的特化

在这里插入图片描述


完整版代码

//setPoint_three.cc

#include <math.h>
#include <iostream>
#include <set>

using std::cout;
using std::endl;
using std::set;

template <typename Container>
void display(const Container &con)
{
    for(auto &elem : con)
    {
        cout << elem << "  ";
    }
    cout << endl;
}

class Point
{
public:
    Point(int ix = 0, int iy = 0)
    : _ix(ix)
    , _iy(iy)
    {
    }

    //计算点到原点的距离
    float getDistance() const
    {
        return hypot(_ix, _iy);
    }

    int getX() const
    {
        return _ix;
    }

    int getY() const
    {
        return _iy;
    }

    ~Point()
    {
    }

    friend std::ostream &operator<<(std::ostream &os, const Point &rhs);
    friend bool operator<(const Point &lhs, const Point &rhs);
    friend struct ComparePoint;

private:
    int _ix;
    int _iy;
};

std::ostream &operator<<(std::ostream &os, const Point &rhs)
{
    os << "(" << rhs._ix
       << ", " << rhs._iy
       << ")";

    return os;
}

bool operator<(const Point &lhs, const Point &rhs)
{
    cout << "bool operator<" << endl;
    if(lhs.getDistance() < rhs.getDistance())
    {
        return true;
    }
    else if(lhs.getDistance() == rhs.getDistance())
    {
        if(lhs._ix < rhs._ix)
        {
            return true;
        }
        else if(lhs._ix == rhs._ix)
        {
            if(lhs._iy < rhs._iy)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

struct ComparePoint
{
    bool operator()(const Point &lhs, const Point  &rhs) const
    {
        cout << "struct ComparePoint" << endl;
        if(lhs.getDistance() < rhs.getDistance())
        {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance())
        {
            if(lhs._ix < rhs._ix)
            {
                return true;
            }
            else if(lhs._ix == rhs._ix)
            {
                if(lhs._iy < rhs._iy)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }

    }
};

#if 0
namespace std
{
template <class T>
struct less
{
    bool operator()(const T &lhs, const T &rhs) const
    {
        return lhs < rhs;
    }
};
}//end of namespace std
#endif

//命名空间的扩展
namespace  std
{

template <>
struct less<Point>//模板的特化
{
    bool operator()(const Point &lhs, const Point &rhs) const
    {
        cout << "template<> struct less" << endl;
        if(lhs.getDistance() < rhs.getDistance())
        {
            return true;
        }
        else if(lhs.getDistance() == rhs.getDistance())
        {
            if(lhs.getX() < rhs.getX())
            {
                return true;
            }
            else if(lhs.getX() == rhs.getX())
            {
                if(lhs.getY() < rhs.getY())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
};

}//end of namespace std
 
void test()
{
    set<Point> number = {
    /* set<Point, ComparePoint> number = { */
        Point(1, 2),
        Point(-1, 2),
        Point(1, 4),
        Point(1, 2),
        Point(1, -2),
        Point(3, 2),
    };
    display(number);
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值