【问题标题】:How to avoid mistakes in operator== implementations in C++?如何避免 C++ 中 operator== 实现中的错误?
【发布时间】:2018-08-14 03:01:43
【问题描述】:

我经常有提供简单的逐个成员比较的类:

class ApplicationSettings
{
public:
   bool operator==(const ApplicationSettings& other) const;
   bool operator!=(const ApplicationSettings& other) const;

private:
   SkinType m_ApplicationSkin;
   UpdateCheckInterval m_IntervalForUpdateChecks;
   bool m_bDockSelectionWidget;
   // Add future members to operator==
};

bool ApplicationSettings::operator==(const ApplicationSettings& other) const
{
   if (m_ApplicationSkin != other.m_ApplicationSkin)
   {
      return false;
   }

   if (m_IntervalForUpdateChecks != other.m_IntervalForUpdateChecks)
   {
      return false;
   }

   if (m_bDockSelectionWidget != other.m_bDockSelectionWidget)
   {
      return false;
   }

   return true;
}

bool ApplicationSettings::operator!=(const ApplicationSettings& other) const;
{
   return ( ! operator==(other));
}

Given that C++ at this time does not provide any construct to generate an operator==,除了我在数据成员下方添加的评论之外,有没有更好的方法来确保未来的成员成为比较的一部分?

【问题讨论】:

  • 未来的 C++ 版本可能会在这方面提供一些帮助。请参阅CppCon2017 Herb Sutter on "Meta: Thoughts on generative C++" 谈话。但是今天我看不到自动检查或生成的方法
  • @nwp - 您仍然需要将新成员添加到对 tie 的调用中。
  • 您还可以编写一个函数,为您提供一个constexpr 指向成员的指针元组,因此您不需要复制成员列表。 SO上有一个问题表明了这一点,但我找不到。它还提到了magic get。
  • 我提到的成员指针元组的This is an example。

标签: c++ operators


【解决方案1】:

只关注技术方面,您可以利用标准库std::tuple 类型重载operator== 的事实进行成员比较。如果您不介意在其他地方牺牲简单的成员访问权限,您可以将您的成员包装在一个元组中。像这样的:

#include <tuple>

class ApplicationSettings
{
public:
   bool operator==(const ApplicationSettings& other) const;
   bool operator!=(const ApplicationSettings& other) const;

private:

   enum m {
     ApplicationSkin, 
     IntervalForUpdateChecks,
     bDockSelectionWidget
   };

   std::tuple<
     SkinType,
     UpdateCheckInterval,
     bool
   > m_Data;
};

现在实现比较运算符很容易:

bool ApplicationSettings::operator==(const ApplicationSettings& other) const {
  m_Data == other.m_Data;
}

当然,牺牲的是其他成员函数需要通过std::get&lt;m::ApplicationSkin&gt;(m_Data)访问其他成员。这可能会引起不少人的注意。

【讨论】:

  • 这甚至应该允许我处理一些极端情况:如果我使用 std::equal_to,我可以提供一个自定义比较模板方法来处理像浮点数这样的特殊成员。此外,我可以以最高效的方式对元组进行排序(首先是简单比较成员)
  • @Asperamanca - 我没有考虑到这一点。你当然是对的。它甚至可以使成员类型从其标识符转变为整数常量的拆分更容易容忍。不过,不要用这个操之过急。仔细考虑权衡。
  • 我认为enum class 不是一个好的选择(模板参数推导/替换失败)。
  • 我不喜欢这个答案 - 我什至不确定它是否真的解决了这个问题。它给不知情的读者增加了复杂性和困惑——“wtf 所有这些东西都是作为元组添加的吗?”它只会让事情变得丑陋。如果我遇到这个类并且需要添加一个成员,我会将它添加到元组中还是只是将它添加为另一个成员?当然,现在我已经阅读了这个答案,我会将其添加到元组中,但我不相信不知情的程序员一定会这样做。
  • @MikeVine - 我写了它,我会犹豫自己推荐它。就像我一开始一样,我只专注于技术。这只是为了展示可行性。如果添加了数据成员,OP 希望操作员不需要更新。就这样。如果您认为这没有用,没有难过的感觉,您可以投反对票。
【解决方案2】:

它不能捕捉所有情况,而且令人讨厌的是它依赖于编译器和平台,但一种方法是基于 sizeof 类型的 static_assert:

static_assert<sizeof(*this) == <n>, "More members added?");

其中&lt;n&gt; 是constexpr。

如果引入了新成员,sizeof 通常会发生变化,并且会导致编译时失败。

【讨论】:

  • 很棒,但是如何计算n?
  • @p-a-o-l-o:调用printf 获取值,然后编辑源代码。是的,这不是很好。
  • 总之还是有用的。
  • 或者,在本地声明一个具有相同成员的虚拟类,并在比较的右侧使用它。这将更便携。
  • 是否可以根据 operator== 中比较的最后一个成员来计算预期的 sizeof(假设这也是类内存布局中的最后一个成员)?它只会使运行时检查成为可能,但至少检查会在有问题的代码所在的位置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-01
  • 2020-07-13
  • 1970-01-01
  • 1970-01-01
  • 2013-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多