stl 比较和boost LessThanComparable
C++ STL的几种常用“比较”概念简述
http://www.boost.org/doc/libs/1_55_0/libs/utility/LessThanComparable.html
A type is LessThanComparable if it is ordered: it must be possible to compare two objects of that type using operator<, and operator< must be a strict weak ordering relation.
http://book.51cto.com/art/201205/335224.htm
4.8.1 基本运算概念
由于C++可重载的操作符很多,因此operators库是由多个类组成的,分别用来实现不同的运算概念,比如less_than_comparable定义了<系列操作符,left_shiftable定义了<<系列操作符。operators中的概念非常多,囊括了C++中的大部分操作符重载,在这里我们先介绍一些最常用的算术操作符:
equality_comparable:要求提供==,可自动实现!=,相等语义;
less_than_comparable:要求提供<,可自动实现>、<=、>=;
addable:要求提供+=,可自动实现+;
subtractable:要求提供-=,可自动实现-;
incrementable:要求提供前置++,可自动实现后置++;
decrementable:要求提供前置--,可自动实现后置--。
equivalent:要求提供<,可自动实现==,等价语义,它与equality_comparable的区别请参见4.8.5节。
这些概念在库中以同名类的形式提供,用户需要以继承的方式来使用它们。继承的修饰符并不重要(private、public都可以),因为operators库里的类都是空类,没有成员变量和成员函数,仅定义了数个友元操作符函数。
例如,less_than_comparable的形式是:
- template <class T>
- struct less_than_comparable {
- friend bool operator>(const T& x, const T& y);
- friend bool operator<=(const T& x, const T& y);
- friend bool operator>=(const T& x, const T& y);
- };
如果要同时实现多个运算概念则可以使用多重继承技术,把自定义类作为多个概念的子类,但多重继承在使用时存在很多问题,稍后将看到operators库使用了特别的技巧来解决这个问题。
我们使用之前4.5.3节定义的三维空间的点point作为opreators库的示范类,在此把它重新定义如下(去掉了swap函数):
- class point
- {
- int x, y, z;
- public:
- explicit point(int a=0, int b=0, int c=0):x(a),y(b),z(c){}
- void print()const
- { cout << x <<","<< y <<","<< z << endl; }
- };
我们先来实现less_than_comparable,它要求point类提供<操作符,并由它继承。假定point的小于关系是三个坐标值的平方和决定的,下面的代码示范了less_than_comparable的用法,只需要为point增加父类,并定义less_than_comparable概念所要求的operator<:
- #include <boost/operators.hpp>
- class point:
- boost::less_than_comparable<point> //小于关系,私有继承
- {
- public:
- friend bool operator<(const point& l, const point& r)
- {
- return (l.x*l.x + l.y*l.y +l.z*l.z <
- r.x*r.x + r.y*r.y +r.z*r.z);
- }
- ... //其他成员函数
- };
less_than_comparable作为基类的用法可能稍微有点奇怪,它把子类point作为了父类的模板参数:less_than_comparable,看起来好像是个"循环继承"。实际上,point类作为less_than_comparable的模板类型参数,只是用来实现内部的比较操作符,用作操作符函数的类型,没有任何继承关系。less_than_comparable生成的代码可以理解成这样 :
- //template<TT = point>
- struct less_than_comparable
- {
- friend bool operator>=(const point& x, const point& y)
- { return !(x < y); }
- }
(
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以
声明,声明时只需在友元的名称前加上关键字friend
)
明白了less_than_comparable的继承用法,剩下的就很简单了:point类定义了一个友元operator<操作符,然后其余的>、<=、>=就由less_than_comparable自动生成。几乎不费什么力气,在没有污染名字空间的情况下我们就获得了四个操作符的能力:
- int main()
- {
- point p0, p1(1,2,3), p2(3,0,5), p3(3,2,1);
- assert(p0 < p1 && p1 < p2);
- assert(p2 > p0);
- assert(p1 <= p3);
- assert(!(p1<p3)&&!(p1>p3) );
- }
同样我们可以定义相等关系,使用equality_comparable,规则是point的三个坐标值完全相等,需要自行实现operator==:
- class point:boost::less_than_comparable<point> //使用多重继承
- ,boost::equality_comparable<point> //新增相等关系
- {
- public:
- friend bool operator<(const point& l, const point& r)
- { /*同前*/ }
- friend bool operator==(const point& l, const point& r)
- { return r.x == l.x && r.y == l.y && r.z == l.z; }
- };
然后我们就自动获得了operator!=的定义:
- point p0, p1(1,2,3), p2(p1), p3(3,2,1);
- assert(p1 == p2);
- assert(p1 != p3);
在使用operators库时要注意一点,模板类型参数必须是子类自身,特别是当子类本身也是个模板类的时候,不要错写成子类的模板参数或者子类不带模板参数的名称,否则会造成编译错误。假如我们改写point类为一个模板类:
- template<typename T> class point {...}
那么如下的形式都是错误的:
- template<typename T> class point:boost::less_than_comparable<T>
- template<typename T> class point:boost::less_than_comparable<point>
正确的写法应该是:
- template<typename T> class point:boost::less_than_comparable< point<T> >
因为只有point才是模板类point的全名。
4.8.3 基类链
多重继承一直是C++中引发争论的话题,喜欢它的人和讨厌它的人几乎同样多。总的来说,多重继承是一种强大的面向对象技术,但使用不当也很容易引发诸多问题,比如难以优化和经典的"钻石型"继承。
operators库使用泛型编程的"基类链"技术解决了多重继承的问题,这种技术通过模板把多继承转换为链式的单继承。
前面当讨论到less_than_comparable<point>这种用法时,我们说它不是继承,然而,现在,我们将看到它居然真的可以实现继承的功能,这从一个方面展示了泛型编程的强大威力。
operators库的操作符模板类除了接受子类作为比较类型外,还可以接受另外一个类,作为它的父类,由此可以无限串联链接在一起(但要受编译器的模板编译能力限制),像这样:
- demo: x< demo, y<demo, z<demo,...> > >
使用基类链技术,point类的基类部分可以是这样:
- boost::less_than_comparable<point, //注意这里
- boost::equality_comparable<point> > //是一个有很大模板参数列表的类
对比一下多重继承的写法:
- boost::less_than_comparable<point>, //注意这里
- boost::equality_comparable<point> //有两个类
代码非常相似,区别仅仅在于模板参数列表结束符号(>)的位置,如果不仔细看可能根本察觉不出差距。但正是这个小小的差距,使基类链通过模板组成了一连串的单继承链表,而不是多个父类的多重继承。
例如,如果为point类再增加加法和减法定义,继承列表就是:
- class point:
- less_than_comparable<point, //小于操作
- equality_comparable<point, //相等操作
- addable<point, //加法操作
- subtractable<point //减法操作
- > > > >
- {...}
基类链技术会导致代码出现一个有趣的形式:在派生类的基类声明末尾处出现一长串的>(模板声明的结束符),在编写代码时需要小心谨慎以保证尖括号的匹配,使用良好的代码缩进和换行可以减少错误的发生。
我的实现less than compare的方法:
下面代码有错误,去掉MyLessThanComparable的继承自T就行了。
class Point2;
template<class T>
struct MYLessThanComparable :T
{
friend bool operator>=(const T& l, const T& r)
{
return !(l < r);
}
friend bool operator>(const T& l, const T& r)
{
return r < l; //这里z注意:r<l,自然l>r
}
friend bool operator<= (const T& l, const T& r)
{
return !(l>r);
} }; class Point2 :MYLessThanComparable<Point2>
{
public:
explicit Point2(int a = , int b = , int c = ) :x(a), y(b), z(c)
{ }
void print() const
{
cout << x << "," << y << "," << z << endl;
} friend bool operator<(const Point2& l, const Point2& r) //友元函数,与这个类无关 //:point类定义了一个友元operator<操作符,然后其余的>、<=、>=就由less_than_comparable自动生成
{
return (l.x*l.x + l.y*l.y + l.z*l.z <
r.x*r.x + r.y*r.y + r.z*r.z); }
private:
int x, y, z;
};
编译报错:
error C2504: “Point2”: 未定义基类
参见对正在编译的类 模板 实例化“MYLessThanComparable<Point2>”的引用
已经声明和定义了Point2,为什么还一直报这个错误,
看了这个帖子:http://bbs.csdn.net/topics/390448140
在定义派生类的头文件上下文中,必须直接或间接地看到基类的定义,单单给出基类的前置声明编译无法通过。
上面的Point2和MYLessThanComparable相互继承,导致出现不管谁在前谁在后,一定有一个会报错:未定义基类,究其原因还是“在定义派生类的头文件上下文中,必须直接或间接的看到基类的定义,单单给出声明时不行的”
后来想了下,MYLessThanComparable不必继承Point2,继承没什么用,去掉就行了。
stl 比较和boost LessThanComparable的更多相关文章
- (转载)STL map与Boost unordered_map的比较
原链接:传送门 今天看到 boost::unordered_map,它与 stl::map的区别就是,stl::map是按照operator<比较判断元素是否相同,以及比较元素的大小,然后选择合 ...
- STL 跨模块 调用 异常 解决
本文为转载别人的,以作收藏之用 百度了一天,现在把结论放上边: 1.不要用STL(std::string属于STL)来跨模块传输数据,例如:dll(so)之间,dll(so)和exe(elf)之间. ...
- DLL中传递STL参数(如Vector或者list等)会遇到的问题[转载]
最近的一个项目中遇到了调用别人的sdk接口(dll库)而传给我的是一个vector指针,用完之后还要我来删除的情况.这个过程中首先就是在我的exe中将其vector指针转为相应指针再获取vector中 ...
- C++ | boost库 类的序列化
是的,这是今年的情人节,一篇还在研究怎么用的文章,文结的时候应该就用成功了. 恩,要有信心 神奇的分割线 不知何时装过boost库的header-only库, 所以ratslam中的boost是可以编 ...
- boost::pool 库速记
使用示例 #include <functional> #include <iostream> #include <boost/pool/pool.hpp> #inc ...
- 一起学习Boost标准库--Boost.StringAlgorithms库
概述 在未使用Boost库时,使用STL的std::string处理一些字符串时,总是不顺手,特别是当用了C#/Python等语言后trim/split总要封装一个方法来处理.如果没有形成自己的com ...
- Boost入门
[转载网友转载的 不过不知道原作者地址] Boost入门向导 简介:boost是一套开源的.高度可移植的C++模板库.它由C++标准委员发起,且里面很多组件有望成为下一代的C++标准库,其地位将会与S ...
- Linus:C++是一种糟糕的语言
本文内容摘自http://blog.csdn.net/turingbook/article/details/1775488 C++是一种糟糕的(horrible)语言.而且因为有大量不够标准的程序员在 ...
- Serializable unordered set
Serializable unordered set 可序列化哈希set #include <boost/algorithm/string/predicate.hpp> #include ...
随机推荐
- 【异常】java.lang.LinkageError: loader constraints violated
[问题背景] 南非客户帐单提醒功能过程中的一个问题,当启动服务器后,后台报java.lang.LinkageError: loader constraints violated when l ...
- 简单 常用的git命令
常用的git命令 git pull 获取最新 git add . 提交所有 git commit -m “我的注释” git status 查看状态 git push origin master ...
- 初识socket
socket也叫套接字,用于通信的一个句柄,描述IP与端口信息,程序通过套接字可以向网络发出请求或者应答网络请求. socket起源与unix,而unix/Linux基本哲学之一就是”一切皆文件“,对 ...
- 转载:C#中的Invoke理解一
在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...
- FileUpload文件上传控件
1.FileUpload控件的主要功能是向指定目录上传文件.FileUpload控件不会自动上传控件,而需要设置相关的事件处理程序,然后在程序中实现文件上传. 2.FileUpload控件常见的属性 ...
- 关于IE9中webdiriver使用autoit上传文件报错
在ie9中, type="file"的元素是通过js打开的 webdirver结合autoit上传文件时,会报拒绝访问的错 sciTE编辑器中是这样写的: #include < ...
- HTTP头部解析
当我们打开一个网页时,浏览器要向网站服务器发送一个HTTP请求头,然后网站服务器根据HTTP请求头的内容生成当次请求的内容发送给浏览器.你明白HTTP请求头的具体含意吗?下面一条条的为你详细解读,先看 ...
- android中的ActionBar和ToolBar
一.ToolBar 1.概述 Google在2015的IO大会上发布了系列的Material Design风格的控件.其中ToolBar是替代ActionBar的控件.由于ActionBar在各个安卓 ...
- jsp...九九乘法表,三角形,菱形
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- cocos2d-x渲染流程
Cocos2Dx之渲染流程 发表于8个月前(2014-08-08 22:46) 阅读(3762) | 评论(2) 17人收藏此文章, 我要收藏 赞2 如何快速提高你的薪资?-实力拍“跳槽吧兄弟”梦 ...