重写比较运算符会导致哪些重大变化?

如何解决重写比较运算符会导致哪些重大变化?

在C ++ 20中有一些关于重写比较运算符的新规则,我正在尝试了解它们的工作方式。我遇到了以下program

struct B {};

struct A
{
    bool operator==(B const&);  // #1
};

bool operator==(B const&,A const&);  // #2

int main()
{
  B{} == A{};  // C++17: calls #2
               // C++20: calls #1
}

实际上破坏了现有代码。我对此感到有些惊讶; #2实际上对我来说仍然看起来更好:p

那么这些新规则如何改变现有代码的含义?

解决方法

该特定方面是一种简单的重写形式,可以反转操作数。主运算符==<=>可以颠倒,辅助运算符!=<><=>= ,可以根据原语进行重写。

可以通过一个相对简单的示例来说明倒车方面。

如果没有特定的B::operator==(A)来处理b == a,则可以使用相反的方法来进行处理:A::operator==(B)。这是有道理的,因为平等是双向关系:(a == b) => (b == a)

另一方面,对

次要运算符的重写涉及使用不同运算符。考虑a > b。如果您找不到直接执行此操作的功能,例如A::operator>(B),则该语言将开始寻找A::operator<=>(B)之类的东西,然后简单地从中计算结果。

这是一个简单的过程视图,但是我的大多数学生似乎都知道。如果您想了解更多详细信息,请参见C ++ 20的[over.match.oper]部分,这是重载分辨率的一部分(@是运算符的占位符):

对于关系运算符和相等运算符,重写的候选者包括运算符<=>的所有成员,非成员和内置候选者,对于这些运算符,重写表达式(x <=> y) @ 0使用operator<=>

对于关系,相等和三元比较运算符,重写的候选还包括一个合成的候选,对于每个成员,非成员和内置候选,两个参数的顺序颠倒了。 运算符<=>,其重写表达式0 @ (y <=> x)使用该operator<=>格式正确。


从此过去了,必须提供真实的operator==operator<,然后进行锅炉电镀的日子:

operator!=      as      !  operator==
operator>       as      ! (operator== || operator<)
operator<=      as         operator== || operator<
operator>=      as      !  operator<

如果我弄错了一个或多个错误,请不要抱怨,这只说明了我对C ++ 20更好的看法,因为您现在只需要提供最少的一组(很可能是{{ 1}}以及其他任何需要提高效率的内容),然后让编译器来照顾它:-)


可以通过以下代码辨别为什么一个人比另一个人被选中的问题:

operator<=>

那的输出表明#include <iostream> struct B {}; struct A { bool operator==(B const&) { std::cout << "1\n"; return true; } }; bool operator==(B const&,A const&) { std::cout << "2\n"; return true; } int main() { auto b = B{}; auto a = A{}; b == a; // outputs: 1 (const B)b == a; // 1 b == (const A)a; // 2 (const B)b == (const A)a; // 2 } 的{​​{1}}的性质决定了哪个更好。

顺便说一句,您可能想看看this article,它提供了更深入的了解。

,

从非语言律师的角度来看,它的工作原理是这样的。 C ++ 20要求operator==计算两个对象是否相等。相等的概念是可交换的:如果A == B,则B ==A。因此,如果C ++ 20的参数反转规则可以调用两个operator==函数,则您的代码应具有完全相同的任何一种方式。

基本上,C ++ 20所说的是,如果要调用的那个问题很重要,那么您就错误地定义了“平等”。


因此,让我们进入细节。所谓“细节”,是指标准中最恐怖的一章:函数重载解析。

[over.match.oper]/3定义了用于为操作员重载构建候选功能集的机制。 C ++ 20通过引入"rewritten candidates"来添加此功能:一组候选函数,这些候选函数是通过以C ++ 20认为在逻辑上等效的方式重写表达式而发现的。这仅适用于关系和不等式运算符。

该集合的构建符合以下条件:

  • 对于关系([expr.rel])运算符,重写的候选对象包括表达式x y的所有未重写的候选对象。
  • 对于关系([expr.rel])和三向比较([expr.spaceship])运算符,对于每个未重写的变量,重写的候选还包括一个合成的候选,两个参数的顺序相反表达式y x的候选。
  • 对于!=运算符([expr.eq]),重写的候选对象包括表达式x == y的所有未重写的候选对象。
  • 对于等式运算符,对于每个未重写的表达式y == x,重写的候选者还包括一个合成的候选者,两个参数的顺序相反。
  • 对于所有其他运算符,重写的候选集为空。

请注意“综合候选人”的特定概念。这是“反转参数”的标准说法。

本节的其余部分详细说明了如果选择了一位改写的候选者(又名:如何合成呼叫)意味着什么。要找到被选中的候选人,我们必须深入研究C ++标准最恐怖的一章中最恐怖的部分:

Best viable function matching

这是重要的声明:

如果对于所有参数i,ICSi(F1)的转换顺序都不比ICSi(F2)差,则将一个可行函数F1定义为比另一个可行函数F2更好的函数,并且然后

那很重要,因为this。从字面上看。

根据[over.ics.scs]的规则,身份转换比添加限定词的转换更好。

A{}是一个prvalue,而...不是const。成员函数的this参数也不是。因此,这是一个身份转换,这比去往非成员函数const A&的转换顺序要好。

是的,还有一条规则可以更明确地使候选列表中的重写函数不可行。但这无关紧要,因为重写的调用与 alone 函数参数更匹配。

如果您使用显式变量并像这样A const a{};声明一个变量,则[over.match.best]/2.8会介入并取消优先于重写的版本。 As seen here.同样,如果您使成员函数const,您也get consistent behavior

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-