我知道 C++ 不能operator==
自动为一个类定义,但为什么它不能!(a == b)
用于a != b
什么时候operator!=
不可用,但它是可用operator==
的?
我知道,std::rel_ops
虽然我在今天之前没有听说过。
我知道 C++ 不能operator==
自动为一个类定义,但为什么它不能!(a == b)
用于a != b
什么时候operator!=
不可用,但它是可用operator==
的?
我知道,std::rel_ops
虽然我在今天之前没有听说过。
因为operator==
并不一定意味着相反operator!=
。
我想不出任何operator==
不意味着的实例!operator!=
,但它们是单独的运算符。关于 C++ 最自由,有时也是最令人沮丧的事情之一是 C++ 对如何编写代码应用了一组最小的限制。如果您有一个operator==
与 不相反的实例operator!=
,那么您应该能够在 C++ 中表达它。而且,事实上,你可以。
在 C++ 中,你把好与坏放在一起。您可能会认为这是在“坏”的集合中。
请记住,在绝大多数情况下,正确operator!=
实施operator==
.
bool Gizmo::operator!=(const Gizmo& rhs) const
{
return !operator==(rhs);
}
C++ 作为一种语言不提供您没有明确要求的功能。我知道这种理念在默认构造函数之类的情况下有些破绽,但这是 Stroustrup 很早就做出的设计决定——你不用为不用的东西买单。所以编译器不会自动生成你没有要求的东西。
ACCU 网站上提到了1993 年初 Bjarne 的电子邮件链,其中提到了这一点。如果我没记错的话,它也在 D&E 中;我没有方便参考的副本。
语言不允许做你想做的事。operator==
并且operator!=
是两个不同的运算符。我想不出一个例子!(x==y)
会x!=y
产生不同的结果,但考虑operator<=
vs operator>
。为什么你需要这两个?可以写成x<=y
,!(x>y)
对吧?错误的。
#include<iostream>
int main () {
double y = 0.0;
double x = y/y;
std::cout << " (x <= y) -> " << (x <= y) << "\n";
std::cout << "!(x > y) -> " << !(x > y) << "\n";
}
C++ 的第一个版本(更正后的 C++03)引入了默认构造函数、复制构造函数、复制赋值运算符和析构函数的自动定义,以便能够在 C++ 中编译 C。
事实证明,尽管它可能不是最佳选择,但许多为析构函数提供自定义定义的人忘记定义复制构造函数和赋值运算符,结果一团糟。
隐式方法,如隐藏的执行路径,似乎让开发人员感到困惑。我想我们都被咬了。
然而,C++11 有一个非常聪明的按需默认方法机制:
class Test { Test() = default; }; // a rather useless class...
所以,吸取 C++03 自动生成构造函数的经验,我不赞成引入这种自动生成,但肯定会支持:
bool operator!=(Test const&, Test const&) = default;
(operator==
显然在范围内)
同样:
bool operator>(Test const&, Test const&) = default;
bool operator<=(Test const&, Test const&) = default;
bool operator>=(Test const&, Test const&) = default;
(operator<
显然在范围内)
然而,我们可能会问真正的问题:为什么不提供一个更通用的方法?
operator==
一般不会搞砸,但我见过无数的operator<
. 显然尊重弱顺序并不像看起来那么容易(*)。尽管如此,如果你认为元组,它只是两个元组的字典比较,真的!
(*)你有==
and<
的实现不匹配(即通常!(a < b) and !(b < a)
<=> a == b
),但元组确实解决了这个问题!
实际上:
std::tuple<int, std::string const&> to_tuple(Test const&);
通常可用于生成初始运算符:
template <typename T>
auto operator==(T const& left, T const& right) -> decltype(to_tuple(left), bool{}) {
return to_tuple(left) == to_tuple(right);
}
template <typename T>
auto operator<(T const& left, T const& right) -> delctype(to_tuple(right), bool{}) {
return to_tuple(left) < to_tuple(right);
}
那么,陷阱是什么?好吧,ADL。当这些模板与您实现的类位于不同的命名空间to_tuple
中时,事情就会分崩离析,因为它们不会被 ADL 自动拾取(同样的原因,虽然using std::swap
很常见......)。
因此,我们可以争辩说,如果在范围内,bool operator==(Test const&, Test const&) = default;
应该做正确的事情(tm) 。to_tuple(Test const&)
它甚至不会是疯狂的。没有太多。
但是,看看我离最初的提议有多远?想象一下委员会的决定最终会是什么......
与此同时?
好吧,就我个人而言,我实现:
#define MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, Op_) \
inline bool operator Op_ (Type_ const& left, Type_ const& right) { \
return to_tuple(left) Op_ to_tuple(right); \
} \
#define MY_DEFINE_TUPLE_EQUAL(Type_) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, ==) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, !=)
#define MY_DEFINE_TUPLE_COMP(Type_) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, <) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, >) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, <=) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, >=)
进而:
class Test;
std::tuple<int, std::string const&> to_tuple(Test const&); // or boost::tuple
MY_DEFINE_TUPLE_EQUAL(Test);
MY_DEFINE_TUPLE_COMP(Test);
它与 ADL 一起使用,它生成内联代码to_tuple
(可能是内联代码本身也可能不是内联代码),它生成正确且一致==
的<
实现,并且它比= default
所有 6 种方法的输入更少。
甚至为编译器错误消息留下源位置!
那么......为什么确实使语言进一步复杂化?