c++20默认比较运算符是一个非常方便的特性。但是我发现如果这个类有一个空的基类,那么它就不太有用了。
默认的operator<=>通过依次比较T的基(从左到右深度-首先)和非静态成员(按声明顺序排列)来计算<=>,递归展开数组成员(按下标的顺序),并在发现不相等的结果时尽早停止。
根据标准,如果SComparable
没有operator<=>,那么base
就不会有operator<=>。在我看来,为空类定义比较操作符是没有意义的。因此,默认的比较运算符不适用于具有空基类的类。
struct base {};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default; // default deleted, clang gives a warning
};
struct SNotComparable: base {
int m_n;
};
如果我们迫切希望使用默认的比较运算符,并因此为空基类base
定义比较运算符。另一个派生类SNotComparable
由于其空基类base
而变得可比较错误。
struct base {
auto operator<=>(base const&) const& = default;
};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable: base { // SNotComparable is wrongly comparable!
int m_n;
};
那么,对于基类为空的类使用默认比较运算符,推荐的解决方案是什么?
编辑:一些答案建议在空基类中添加默认的比较运算符,并在不可比较的派生类中显式删除比较运算符。
如果我们将默认比较运算符添加到一个非常常用的空基类中,那么突然间,它的所有不可比较的派生类都是可比较的(总是返回std::strong_ordering::equal)。我们必须找到所有这些派生的不可比较类,并显式删除它们的比较运算符。如果我们遗漏了某个类,以后想使它具有可比性,但忘记自定义它的比较操作符(我们都会出错),我们就会得到一个错误的结果,而不是像以前一样在空基中没有默认的比较操作符,而不是编译错误。那么,为什么首先使用默认的比较运算符?我想省去一些努力,而不是多介绍一些。
struct base {
auto operator<=>(base const&) const& = default;
};
struct SComparable: base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable1: base {
int m_n;
auto operator<=>(SNotComparable1 const&) const& = delete;
};
struct SNotComparableN: base {
int m_n;
// oops, forget to delete the comparison operator!
// if later we want to make this class comparable but forget to customize comparison operator, we get a wrong result instead of a non-comparable compile error.
};
发布于 2022-08-25 11:16:39
我想做一个基于@Barry's answer的小修改。我们可以有一个通用的混合类comparable<EmptyBase>
,为任何空基提供类似的操作符。如果我们想对从空基类(Es)派生的类使用默认比较运算符,我们可以简单地从comparable<base>
而不是base
派生此类类。它也适用于链式空基comparable<base1<base2>>
。
struct base { /* ... */ };
template<typename EmptyBase>
struct comparable: EmptyBase {
static_assert(std::is_empty<EmptyBase>::value);
template<typename T> requires std::same_as<comparable>
friend constexpr auto operator==(T const&, T const&)
-> bool
{
return true;
}
template<typename T> requires std::same_as<comparable>
friend constexpr auto operator<=>(T const&, T const&)
-> std::strong_ordering
{
return std::strong_ordering::equal;
}
};
struct SComparableDefault: comparable<base> {
int m_n;
auto operator<=>(SComparableDefault const&) const& = default;
};
struct SNotComparable: base {
int m_n;
};
struct SComparableNotDefault: base {
int m_n;
constexpr bool operator==(SComparableNotDefault const& rhs) const& {
/* user defined... */
}
constexpr auto operator<=>(SComparableNotDefault const& rhs) const& {
/* user defined... */
}
};
发布于 2022-08-25 07:02:58
在我看来,为空类定义比较操作符是没有意义的。
很明显这不是毫无意义的。如果要做的是默认类型的比较,那必然意味着比较所有类型的子对象,包括基类子对象,这要求它们是可比较的--即使它们是空的。
你需要做的是提供-只是有条件的。最简单的方法可能是提供一个不同的空基类:
struct base { /* ... */ };
struct comparable_base : base {
friend constexpr auto operator==(comparable_base, comparable_base)
-> bool
{
return true;
}
friend constexpr auto operator<=>(comparable_base, comparable_base)
-> std::strong_ordering
{
return std::strong_ordering::equal;
}
};
然后,当您想要进行比较时,从comparable_base
继承,当不想进行比较时,继承base
。
struct SComparable: comparable_base {
int m_n;
auto operator<=>(SComparable const&) const& = default;
};
struct SNotComparable: base {
int m_n;
};
我在那里使用隐藏的朋友比较,因为它是空的,所以我可以按值取类型。也很容易成为会员。
发布于 2022-08-25 03:31:27
对于带有空基类的类使用默认比较运算符的推荐解决方案是什么?
解决方案是将默认的比较器添加到基类中,然后在SComparable
中执行您所做的事情,如果您希望在比较中包含SComparable
的添加成员--就像使用成员的基类一样。
如果不希望它们包含在比较中,就不要像在SNotComparable
中那样添加默认的比较器--而且基类比较器也将被使用--就像在带有成员的基类中一样。
如果您不希望SNotComparable
中的基类行为并且不希望SNotComparable
是可比较的,那么就像基类有成员时一样,delete
是比较器:
auto operator<=>(SNotComparable const&) const& = delete;
https://stackoverflow.com/questions/73485942
复制相似问题