首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >c++20默认比较运算符和空基类

c++20默认比较运算符和空基类
EN

Stack Overflow用户
提问于 2022-08-25 10:29:32
回答 3查看 150关注 0票数 3

c++20默认比较运算符是一个非常方便的特性。但是我发现如果这个类有一个空的基类,那么它就不太有用了。

默认的operator<=>通过依次比较T的基(从左到右深度-首先)和非静态成员(按声明顺序排列)来计算<=>,递归展开数组成员(按下标的顺序),并在发现不相等的结果时尽早停止。

根据标准,如果SComparable没有operator<=>,那么base就不会有operator<=>。在我看来,为空类定义比较操作符是没有意义的。因此,默认的比较运算符不适用于具有空基类的类。

代码语言:javascript
运行
复制
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而变得可比较错误。

代码语言:javascript
运行
复制
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)。我们必须找到所有这些派生的不可比较类,并显式删除它们的比较运算符。如果我们遗漏了某个类,以后想使它具有可比性,但忘记自定义它的比较操作符(我们都会出错),我们就会得到一个错误的结果,而不是像以前一样在空基中没有默认的比较操作符,而不是编译错误。那么,为什么首先使用默认的比较运算符?我想省去一些努力,而不是多介绍一些。

代码语言:javascript
运行
复制
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.
};
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-08-25 19:16:39

我想做一个基于@Barry's answer的小修改。我们可以有一个通用的混合类comparable<EmptyBase>,为任何空基提供类似的操作符。如果我们想对从空基类(Es)派生的类使用默认比较运算符,我们可以简单地从comparable<base>而不是base派生此类类。它也适用于链式空基comparable<base1<base2>>

代码语言:javascript
运行
复制
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... */
  }
};
票数 1
EN

Stack Overflow用户

发布于 2022-08-25 15:02:58

在我看来,为空类定义比较操作符是没有意义的。

很明显这不是毫无意义的。如果要做的是默认类型的比较,那必然意味着比较所有类型的子对象,包括基类子对象,这要求它们是可比较的--即使它们是空的。

你需要做的是提供-只是有条件的。最简单的方法可能是提供一个不同的空基类:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
struct SComparable: comparable_base {
  int m_n;
  auto operator<=>(SComparable const&) const& = default;
};

struct SNotComparable: base {
  int m_n;
};

我在那里使用隐藏的朋友比较,因为它是空的,所以我可以按值取类型。也很容易成为会员。

票数 2
EN

Stack Overflow用户

发布于 2022-08-25 11:31:27

对于带有空基类的类使用默认比较运算符的推荐解决方案是什么?

解决方案是将默认的比较器添加到基类中,然后在SComparable中执行您所做的事情,如果您希望在比较中包含SComparable的添加成员--就像使用成员的基类一样。

如果不希望它们包含在比较中,就不要像在SNotComparable中那样添加默认的比较器--而且基类比较器也将被使用--就像在带有成员的基类中一样。

如果您不希望SNotComparable中的基类行为并且不希望SNotComparable是可比较的,那么就像基类有成员时一样,delete是比较器:

代码语言:javascript
运行
复制
auto operator<=>(SNotComparable const&) const& = delete;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73485942

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档