[英]Equality operator overloads: Is (x!=y) == (!(x==y))?
C++ 标准是否保证(x!=y)
始终与!(x==y)
具有相同的真值?
我知道这里涉及许多微妙之处:运算符==
和!=
可能会被重载。 它们可能被重载以具有不同的返回类型(只需隐式转换为bool
)。 甚至!
-operator 可能在返回类型上重载。 这就是为什么我手摇地提到上面的“真值”,但试图进一步阐述它,利用隐式转换为bool
,并试图消除可能的歧义:
bool ne = (x!=y);
bool e = (x==y);
bool result = (ne == (!e));
result
在这里保证是true
吗?
C++ 标准在 5.10 节中指定了相等运算符,但似乎主要是在语法上定义它们(以及一些关于指针比较的语义)。 存在EqualityComparable的概念,但没有专门声明其运算符==
与!=
运算符的关系。
有来自 C++ 工作组的相关文档,说...
重要的是,相等/不等 [...] 表现为彼此的布尔否定。 毕竟,如果 operator==() 和 operator!=() 都返回 false,那么这个世界将毫无意义! 因此,在彼此方面实现这些运算符是很常见的
但是,这仅反映了 Common Sense™,并没有指定它们必须像这样实现。
一些背景:我只是想写一个函数来检查两个值(未知类型)是否相等,如果不是,则打印一条错误消息。 我想说这里需要的概念是类型是EqualityComparable
。 但是为此,仍然必须写if (!(x==y)) {…}
并且不能写if (x!=y) {…}
,因为这将使用不同的运算符,这未涵盖完全使用EqualityComparable
的概念,甚至可能以不同的方式重载......
我知道程序员基本上可以在他的自定义重载中做任何他想做的事情。 我只是想知道他是否真的可以做任何事情,或者是否有标准强加的规则。 也许这些微妙的陈述之一表明偏离通常的实现会导致未定义的行为,就像NathanOliver 在评论中提到的那样,但似乎只涉及某些类型。 例如,标准明确规定,对于容器类型, a!=b
等效于!(a==b)
(第 23.2.1 节,表 95,“容器要求”)。
但是对于一般的、用户自定义的类型,目前好像没有这样的要求。 这个问题被标记为language-lawyer
,因为我希望得到一个明确的陈述/参考,但我知道这几乎是不可能的:虽然人们可以指出它说运营商必须相互否定的部分,但很难证明标准的约 1500 页都没有说这样的话......
有疑问,除非有进一步的提示,否则我稍后会赞成/接受相应的答案,现在假设比较EqualityComparable
类型的EqualityComparable
应该使用if (!(x==y))
来完成在安全方面。
C++ 标准是否保证
(x!=y)
始终与!(x==y)
具有相同的真值?
不,它没有。 绝对没有什么能阻止我写:
struct Broken {
bool operator==(const Broken& ) const { return true; }
bool operator!=(const Broken& ) const { return true; }
};
Broken x, y;
那是完全格式良好的代码。 从语义上讲,它被破坏了(顾名思义),但从纯 C++ 代码功能的角度来看,它肯定没有错。
该标准还清楚地表明在[over.oper]/7 中这是可以的:
应用于基本类型(例如,
++a ≡ a+=1
)的某些预定义运算符之间的身份不需要对运算符函数保持不变。 一些预定义的运算符(例如+=
在应用于基本类型时要求操作数为左值; 这不是操作员功能所要求的。
同样,C++ 标准中没有任何内容保证operator<
实际上实现了有效的排序(或x<y <==> !(x>=y)
等)。 一些标准库实现实际上会添加检测以尝试在有序容器中为您调试,但这只是实现质量问题,而不是基于标准的决策。
像Boost.Operators这样的库解决方案的存在至少可以让程序员更容易一些:
struct Fixed : equality_comparable<Fixed> {
bool operator==(const Fixed&) const;
// a consistent operator!= is provided for you
};
在 C++14 中, Fixed
不再是与基类的聚合。 但是,在 C++17 中,它又是一个聚合(通过P0017 )。
随着 C++20 采用P1185 ,库解决方案已经有效地变成了一种语言解决方案——你只需要这样写:
struct Fixed {
bool operator==(Fixed const&) const;
};
bool ne(Fixed const& x, Fixed const& y) {
return x != y;
}
ne()
的主体成为计算为!x.operator==(y)
的有效表达式——因此您不必担心保持两个比较一致,也不必依赖库解决方案来提供帮助。
一般而言,我认为您不能依赖它,因为operator ==
和operator!=
始终对应并不总是有意义,所以我不知道标准如何要求它。
例如,考虑内置浮点类型,如双精度数, NaN总是比较为假,因此 operator== 和 operator!= 可以同时返回假。 (编辑:哎呀,这是错误的;请参阅 hvd 的评论。)
因此,如果我正在编写一个具有浮点语义的新类(可能是 real_long_double),我必须实现相同的行为以与原始类型保持一致,因此我的operator==
必须表现相同并进行比较两个 NaN 为假,即使operator!=
也将它们比较为假。
这也可能在其他情况下出现。 例如,如果我正在编写一个类来表示一个数据库可空值,我可能会遇到同样的问题,因为与数据库 NULL 的所有比较都是假的。 我可能会选择在我的 C++ 代码中实现该逻辑以具有与数据库相同的语义。
但是,实际上,对于您的用例,可能不值得担心这些边缘情况。 只需记录您的函数使用operator== (or operator !=)
比较对象,然后将其保留。
不可以。您可以为==
和!=
编写运算符重载来做任何您想做的事。 这样做可能是个坏主意,但 C++ 的定义并没有将这些运算符限制为彼此的逻辑对立。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.