Boost开发指南-4.8operators
operators
C++提供了强大且自由的操作符重载能力,可以把大多数操作符重新定义为函数,使操作更加简单直观。这方面很好的例子就是标准库中的string和 complex,可以像操作内置类型int、double那样对它们进行算术运算和比较运算,非常方便。
但实现重载操作符却比使用它要麻烦许多,因为很多运算具有对称性,如果定义了operator+,那么很自然需要operator-,如果有小于比较,那么也应该有小于等于、大于、大于等于比较。完全实现这些操作符的重载工作是单调乏味的,而且增加的代码量也增加了出错的可能性,还必须保证这些操作符都实现了正确的语义。
实际上很多操作符可以从其他的操作符自动推导出来,例如a !=b可以是!(a==b),a>=b可以是!(a<b)。因此原则上只需要定义少量的基本操作符,其他的操作符就可以用逻辑组合实现。
在c++标准的std::rel_ops名字空间里提供了四个模板比较操作符 !=、>、<=、>=,只需要为类定义了==和<操作符,那么这四个操作符就可以自动实现。
#include <utility>
class demo_class //一个定义operator<的类
{
public:demo_class(int n) :x(n) {}int x;friend bool operator<(const demo_class& l, const demo_class& r){return l.x < r.x;}
};void case1()
{demo_class a(10), b(20);using namespace std::rel_ops; //打开std::rel_ops名字空间assert(a < b); //自定义的<操作符assert(b >= a); //>=等操作符被自动实现
}
但std::rel_ops的解决方案过于简单,还很不够。除了比较操作符,还有很多其他的操作符重载标准库没有给出解决方案,而且使用这些操作符需要用using 语句导入std::rel_ops名字空间,不方便,也会带来潜在的冲突风险。
boost.operators库因此应运而生。它采用类似std::rel_ops 的实现手法,允许用户在自己的类里仅定义少量的操作符(如<),就可方便地自动生成其他操作符重载,而且保证正确的语义实现。
operators位于名字空间boost,为了使用operators组件,需要包含头文件<boost/operators.hpp>,即:
#include <boost/operators.hpp>
using namespace boost;
基本运算概念
由于C++可重载的操作符非常多,因此 operators库是由多个类组成的,分别用来实现不同的运算概念,比如 less_than_comparable定义了<系列操作符,left_shiftable定义了<<系列操作符。
operators中的概念很多,包括了C++中的大部分操作符重载,在这里我们先介绍一些最常用的算术操作符:
equality_comparable : 要求提供==, 可自动实现!=, 相等语义;
less_than_comparable : 要求提供<, 可自动实现>、<=、>=:
addable : 要求提供+=, 可自动实现+;
subtractable : 要求提供-=, 可自动实现-;
incrementable : 要求提供前置++, 可自动实现后置++;
decrementable : 要求提供前置--, 可自动实现后置--;
equivalent : 要求提供<, 可自动实现-=, 等价语义。
这些概念在库中以同名类的形式提供,用户需要以继承的方式来使用它们。继承的修饰符并不重要(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库使用了特别的技巧来解决这个问题。
算术操作符的用法
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<:
class point : boost::less_than_comparable<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;}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>
,看起来好像是个“循环继承”。实际上,point类作为less_than_comparable的模板类型参数,只是用来实现内部的比较操作符,用做操作符函数的类型,没有任何继承关系。less_than_comparable生成的代码可以理解成这样:
//template<T = point>
struct less_than_comparable
{friend bool operator>=(const point& x, const point& y){ return !(x < y); }
}
明白了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<T>
才是模板类point的全名。
基类链
多重继承一直是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 //减法操作> > > >
{...};
基类链技术会导致代码出现一个有趣的形式:在派生类的基类声明末尾处出现一长串的>(模板声明的结束符),在编写代码时需要小心谨慎以保证尖括号的匹配,使用良好的代码缩进和换行可以减少错误的发生。
相关文章:
Boost开发指南-4.8operators
operators C提供了强大且自由的操作符重载能力,可以把大多数操作符重新定义为函数,使操作更加简单直观。这方面很好的例子就是标准库中的string和 complex,可以像操作内置类型int、double那样对它们进行算术运算和比较运算,非常方…...
c# 泛型约束
在C#中,泛型约束用于指定泛型类型参数的限制条件,以确保类型参数满足特定的条件。以下是C#中常见的泛型约束: where T : struct: 这个约束要求类型参数必须是一个值类型(如int、float等)。 where T : cla…...
android frida
Frida 是一个用于动态分析、调试和修改 Android 应用程序的强大工具。它的主要作用包括: 代码注入和Hooking: Frida 允许您在运行时修改和监视应用程序的行为。您可以通过Frida注入JavaScript代码到目标应用程序中,然后使用该代码来Hook&…...
Linux下的Shell编程——正则表达式入门(四)
前言: 正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。 在Linux 中,grep,sed,awk 等文本处理工具都支持…...
使用VisualStudio制作上位机(一)
文章目录 使用VisualStudio制作上位机(一)写在前面第一部分:创建应用程序第二部分:GUI主界面设计使用VisualStudio制作上位机(一) Author:YAL 写在前面 1.达到什么目的呢 本文主要讲怎么通过Visual Studio 制作上位机,全文会以制作过程来介绍怎么做,不会去讲解具体…...
【前端从0开始】JavaSript——自定义函数
函数 函数是一个可重用的代码块,用来完成某个特定功能。每当需要反复执行一段代码时,可以利用函数来避免重复书写相同代码。函数包含着的代码只能在函数被调用时才会执行,就可以避免页面载入时执行该脚本在JavaScript中,可以使用…...
如何在Windows、Mac和Linux操作系统上安装Protocol Buffers(protobuf)编译器
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
简单介绍 CPU 的工作原理
内部架构 CPU 的根本任务就是执行指令,对计算机来说最终都是一串由 0 和 1 组成的序列。CPU 从逻辑上可以划分成 3 个模块,分别是控制单元、运算单元和存储单元 。其内部架构如下: 【1】控制单元 控制单元是整个CPU的指挥控制中心ÿ…...
UE4/5数字人MetaHuman的控制绑定资产使用
目录 开始操作 找到控制绑定资产 放入控制绑定资产 编辑 生成动画资产 开始操作 首先我们创建一个关卡序列: 打开后将我们的数字人放进去【右键,第一个添加进去】: 我们会自动进入动画模式,没有的话,就自己…...
二、11.系统交互
fork 函数原型是 pid_t fork(void),返回值是数字,该数字有可能是子进程的 pid ,有可能是 0,也有可能是-1 。 1个函数有 3 种返回值,这是为什么呢?可能的原因是 Linux 中没有获取子进程 pid 的方…...
敏捷管理工具/国内软件敏捷开发工具
Scrum中非常强调公开、透明、直接有效的沟通,这也是“可视化的管理工具”在敏捷开发中如此重要的原因之一。通过“可视化的管理工具”让所有人直观的看到需求,故事,任务之间的流转状态,可以使团队成员更加快速适应敏捷开发流程。…...
Selenium环境+元素定位大法
selenium 与 webdriver Selenium 是一个用于 Web 测试的工具,测试运行在浏览器中,就像真正的用户在手工操作一样。支持所有主流浏览器 WebDriver 就是对浏览器提供的原生API进行封装,使其成为一套更加面向对象的Selenium WebDriver API。 使…...
Vue3 用父子组件通信实现页面页签功能
一、大概流程 二、用到的Vue3知识 1、组件通信 (1)父给子 在vue3中父组件给子组件传值用到绑定和props 因为页签的数组要放在父页面中, data(){return {tabs: []}}, 所以顶部栏需要向父页面获取页签数组 先在页签页面中定义props用来接…...
HCIP STP协议
STP协议 STP协议概念生成树为什么要用STP STP名词解释根网桥根端口指定端口非指定端口 STP的版本802.1DPVSTPVST 快速生成树 STP协议概念 IEEE 802.1d STP(生成树协议,Spanning-Tree Protocol)协议: ①使冗余端口置于“阻塞状态”…...
链表的顶级理解
目录 1.链表的概念及结构 2.链表的分类 单向或者双向 带头或者不带头 循环或者非循环 3.无头单向非循环链表的实现 3.1创建单链表 3.2遍历链表 3.3得到单链表的长度 3.4查找是否包含关键字 3.5头插法 3.6尾插法 3.7任意位置插入 3.8删除第一次出现关键字为key的节点 …...
探索贪心算法:理解与实现JAVA语言
探索贪心算法:理解与实现 贪心算法(Greedy Algorithm)是一种基于每一步的最优选择来达到整体最优的算法思想。尽管贪心算法并不适用于所有问题,但它在很多情况下都能够提供高效、近似的解决方案。本文将深入探讨贪心算法的基本概…...
数字孪生技术对旅游行业能起到什么作用?
随着疫情对我们生活影响的淡化,旅游行业迎来了新的春天,暑期更是旅游行业的小高潮,那么作为一个钻研数字孪生行业的小白,本文就着旅游的话题以及对旅游的渴望带大家一起探讨一下数字孪生对智慧旅游发展的作用~ 数字孪生作为一种虚…...
攻防世界-Web_php_include
原题 解题思路 php://被替换了,但是只做了一次比对,改大小写就可以绕过。 用burp抓包,看看有哪些文件 flag明显在第一个PHP文件里,直接看...
Python Opencv实践 - 直方图显示
import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape)#图像直方图计算 #cv.calcHist(images, channels, mask, histSize, ranges, hist, accumulate) #images&…...
2分钟搭建自己的GPT网站
如果觉得官方免费的gpt(3.5)体验比较差,总是断开,或者不会fanqiang,那你可以自己搭建一个。但前提是你得有gpt apikey。年初注册的还有18美金的额度,4.1号后注册的就没有额度了。不过也可以自己充值。 有了…...
deepdiff比较两个json文件数据差异性
deepdiff比较两个json文件数据差异性 Python代码片: import json import sysfrom deepdiff import DeepDiff from deepdiff import grep, DeepSearch from deepdiff import DeepHash# print(DeepDiff("abc", "abcd", ignore_orderTrue))class …...
文件内容搜索工具 - Python实现
在本篇文章中,我们将介绍如何使用 wxPython 库创建一个简单的文件搜索工具。这个工具允许用户选择一个文件夹,并在该文件夹中的所有 .py 文件中查找指定的文字,并显示匹配的位置。 C:\pythoncode\blog\searchwordinpyfile.py 代码实现 我们首…...
vue静态html加载外部组件
当我们在开发vue应用时, 使用的是html页面开发, 需要引用外部vue组件, 怎么办呢, 首先我们引用http-vue-loader.js文件, 像下面这样: <script src"/assets/javascript/vue.min.js"></script> <script src"/assets/javascript/http-vue-loader.j…...
WebSocket 中的心跳是什么,有什么作用?
在网络应用开发中,WebSocket 是一种重要的通信协议,它允许客户端和服务器之间建立持久性的双向通信连接。然而,为了保持连接的稳定性,WebSocket 中的心跳是一个不可或缺的概念。本文将详细介绍 WebSocket 中的心跳是什么ÿ…...
Android类加载机制
要说Android的类加载机制 ,就离不开 类加载器ClassLoader,它是一个抽象接口 下面这个图还是比较好表达了类加载流程,但如果不看我红色画的线,就会感觉有点乱,需要注意是采用的是双亲委派模式,class加载要先…...
微信小程序列表加载更多
概述 基于小程序开发的列表加载更多例子。 详细 一、前言 基于小程序开发的列表加载更多例子。 二、运行效果 运行效果(演示的小视频,点击播放即可) 三、实现过程 总体思路如何: 1、通过scroll-view组件提供的bindscroll方法…...
数据库知识
怎么做 常见的数据库 Oracle Mysql SOLSever Navicat (新版可以链接mysql oracle) http://sqlfiddle.com/ 数据库操作在线练习 mysql自带四个数据库 数据库语言的使用 显示数据库:show databases; 创建数据库:…...
VUE 目录介绍
更新升级(npm - i)之后最终目录如下: total 1672 drwxr-xr-x 18 testrose staff 576 8 22 02:53 . drwxr-xr-x 24 testrose staff 768 8 22 02:50 .. -rw-r--r-- 1 testrose staff 402 8 22 02:52 .babelrc -rw…...
Selenium的基本使用
文章目录 引入一.选择元素的基本方法1.根据id 选择元素2.根据 class属性选择元素当元素有 多个class类型 时 3.根据 tag名 选择元素4.通过WebElement对象选择元素5.find_element 和 find_elements 的区别 二.等待界面元素出现1.隐式等待2.显示等待 三.操控元素的基本方法1.点击…...
数据结构-----树的易错点
1.树的度和m叉树 •度为m的树(度表示该结点有多少个孩子(分支)) 任意结点的度<m(最多m个孩子) 至少又一个结点度m(有m个孩子) 一定是非空树,至少有m1个结点 •m叉树 任意结点的度<m(最多有m个孩子) 允许所…...
做网站导航栏目怎么做/什么是网络营销含义
1.div的visibility可以控制div的显示和隐藏,但是隐藏后页面显示空白 document.getElementById("Have_name").style.visibility"visible";document.getElementById("No_name").style.visibility"hidden";2.通过设置display属…...
外贸公司网站源码/百度搜索资源平台提交
smarty中section用法详解作者:小涵 | 来源:互联网 | 2013-06-25 09:49阅读: 6696在smarty中用于循环的有section和foreach,根据个人的习惯不同而选择哪个。foreach在php中就有了,而section的产生是为解决foreach的不足的ÿ…...
郑州富士康最新招聘/百度关键词优化公司
有些网络应用在网线断开后重新连上的情况下tcp socket连接保持ESTABLISH状态不变,假如应用程式不使用tcp的keepalive,在网线断开之后,以前建立的 socket 链接仍然会保持在ESTABLISH 状态不会改变。实际上tcp协议对这部分是有所处理的…...
新手学做网站 pdf下载/百度seo公司电话
啥?你把CUDA系列学习(一),(二)都看完了还不知道為什麼要用GPU提速? 是啊。。经微博上的反馈我默默感觉到提出这样问题的小伙伴不在少数,但是更多小伙伴应该是看了(一&…...
vs做网站如何调试/企业官网首页设计
日期:2012-8-11 来源:GBin1.com 在之前的文章中,我曾经介绍过5款高清柏油路纹理图案,今天我们准备了更多免费高质量的专业纹理图案给大家。在我们这次介绍的纹理图案中除了有木材、混凝土和背景虚化的设计,还有关于废…...
张家口网站建设zjktao/百度一下移动版首页
什么是高压均质? 均质原理:样品(乳液或悬浮液)在高压下通过均质阀产生剪切、撞击、空穴作用。当样品在高压下通过均质阀,样品加速并在压力下势能转换,产生剪切效应、撞击效应和空穴效应。可应用于…...