在比较 C + + 中的结构时发现 No = = 运算符

比较以下结构的两个实例时,我收到一个错误:

struct MyStruct1 {
MyStruct1(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
my_struct_2(_my_struct_2),
an_int(_an_int)
{}


std::string toString() const;


MyStruct2 my_struct_2;
int an_int;
};

错误是:

错误 C2678: 二进制’= =’: 没有操作符 使用左操作数 类型为‘ myproj: : MyStruct1’(或者 是不可接受的转换)

Why?

161602 次浏览

比较不适用于 C 或 C + + 中的结构体,而是通过字段进行比较。

在 C + + 中,struct默认没有比较运算符,你需要自己编写:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return /* your comparison code goes here */
}

开箱即用,= = 操作符只适用于原语。要使代码正常工作,需要重载结构的 = = 运算符。

您需要为 MyStruct1显式定义 operator ==

struct MyStruct1 {
bool operator == (const MyStruct1 &rhs) const
{ /* your logic for comparision between "*this" and "rhs" */ }
};

Now the == comparison is legal for 2 such objects.

因为您没有为结构编写比较运算符。编译器不会为您生成它,所以如果您想要进行比较,您必须自己编写它。

默认情况下,结构没有 ==运算符。您必须编写自己的实现:

bool MyStruct1::operator==(const MyStruct1 &other) const {
...  // Compare the values, and return a bool result.
}

C + + 20引入了 默认比较,也就是“宇宙飞船”operator<=>,它允许您请求编译器生成的 </<=/==/!=/>=/和/或 >操作符,并且具有明显的/幼稚的(?)实现..。

auto operator<=>(const MyClass&) const = default;

但是你可以为更复杂的情况定制它(下面讨论)。有关语言提案,请参阅 给你,其中包含理由和讨论。这个答案仍然与 C + + 17及更早的版本有关,并且对于什么时候应该自定义 operator<=>的实现也有深入的了解。

It may seem a bit unhelpful of C++ not to have already Standardised this earlier, but often structs/classes have some 排除数据成员 from comparison (e.g. counters, cached results, container capacity, last operation success/error code, cursors), as well as decisions to make about myriad things including but not limited to:

  • 首先要比较哪些字段,例如比较一个特定的 int成员可能会很快消除99% 的不等对象,而一个 map<string,string>成员可能经常有相同的条目并且比较相对昂贵——如果值在运行时加载,程序员可能会有编译器不可能有的洞察力
  • 在比较字符串时: 大小写敏感性、空格和分隔符的等价性、转义约定... ..。
  • 比较浮点数/双精度
  • NaN 浮点值是否应视为相等
  • 比较指针或指向数据(如果是后者,如何知道指针是否指向数组,以及需要比较多少对象/字节)
  • 在比较未排序的容器(例如 vectorlist)时,顺序是否重要,如果重要,在比较之前对它们进行排序是否合适,还是每次进行比较时使用额外的内存对临时容器进行排序
  • 当前有多少数组元素包含应该进行比较的有效值(是否存在大小或哨兵?)
  • 比较 union的哪个成员
  • 正常化: 例如,日期类型可能允许超出范围的月份或月份,或者一个理性/分数对象可能有6/8个,而另一个有3/4个,由于性能原因,他们懒惰地用一个单独的正常化步骤进行修正; 您可能必须决定是否在比较之前触发正常化
  • 当弱点无效时该怎么做
  • 如何处理不实现 operator==本身(但可能有 compare()operator<str()或 getter...)的成员和基础
  • 在读取/比较其他线程可能要更新的数据时,必须采用哪些锁

所以,它有点像 很高兴犯了个错误,直到你明确地考虑到比较对于你的特定结构 rather than letting it compile but not give you a meaningful result at run-time意味着什么。

总之,如果 C + + 允许你在决定一个“幼稚的”逐个成员的 ==测试 曾经是时说 bool operator==() const = default;就好了。!=也是。考虑到多个成员/基础,“默认”<<=>>=的实现似乎没有希望-根据声明的可能顺序进行级联,但不太可能是所需要的,因为成员排序的必要性相互冲突(基础必须在成员之前,按可访问性分组,在依赖使用之前的构建/销毁)。为了更广泛的使用,C + + 需要一个新的数据成员/基础注释系统来指导选择——这将是一件伟大的事情,尽管在标准中,理想情况下加上基于 AST 的用户定义代码生成... 我希望它会发生的一天。

相等运算符的典型实现

一个看似合理的实施方案

很有可能的合理而有效的实现应该是:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int     == rhs.an_int;
}

请注意,这也需要 MyStruct2operator==

这个实现的含义和备选方案将在下面的标题 讨论你的 Mystruct1的细节中讨论。

= = 、 < 、 > < = 等的一致方法

利用 std::tuple的比较操作符比较您自己的类实例很容易——只需使用 std::tie按照所需的比较顺序创建字段引用的元组。从 给你总结我的例子:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) ==
std::tie(rhs.my_struct2, rhs.an_int);
}


inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) <
std::tie(rhs.my_struct2, rhs.an_int);
}


// ...etc...

When you "own" (i.e. can edit, a factor with corporate and 3rd party libs) the class you want to compare, and especially with C++14's preparedness to deduce function return type from the return statement, it's often nicer to add a "tie" member function to the class you want to be able to compare:

auto tie() const { return std::tie(my_struct1, an_int); }

然后,上面的比较简化为:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}

如果您想要一组更完整的比较运算符,我建议使用 升压运算符(搜索 less_than_comparable)。如果由于某种原因它不适合,你可能喜欢也可能不喜欢支持宏 (网上)的想法:

#define TIED_OP(STRUCT, OP, GET_FIELDS) \
inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
{ \
return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
}


#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
TIED_OP(STRUCT, ==, GET_FIELDS) \
TIED_OP(STRUCT, !=, GET_FIELDS) \
TIED_OP(STRUCT, <, GET_FIELDS) \
TIED_OP(STRUCT, <=, GET_FIELDS) \
TIED_OP(STRUCT, >=, GET_FIELDS) \
TIED_OP(STRUCT, >, GET_FIELDS)

... 然后可以用来..。

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)

(C + + 14成员并列版本 给你)

讨论你的 Mystruct1的细节

提供一个独立的与会员 operator==()的选择有暗示..。

独立执行

你要做一个有趣的决定。由于类可以从 MyStruct2隐式构造,所以一个独立的/非成员的 bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)函数将支持..。

my_MyStruct2 == my_MyStruct1

首先从 my_myStruct2创建一个临时 MyStruct1然后进行比较。这肯定会将 MyStruct1::an_int设置为构造函数的默认参数值 -1。取决于你是否在 operator==的实现中包括 an_int比较,一个 MyStruct1可能会或可能不会比较等于一个 MyStruct2,它本身比较等于 MyStruct1my_struct_2成员!此外,创建一个临时 MyStruct1可能是一个非常低效的操作,因为它涉及到将现有的 my_myStruct21成员复制到一个临时成员,只是在比较之后将其丢弃。(当然,您可以通过创建构造函数 my_myStruct23或删除 an_int的默认值来防止这种用于比较的 MyStruct1的隐式构造。)

成员执行情况

如果希望避免从 MyStruct2隐式构造 MyStruct1,请将比较运算符设置为成员函数:

struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};

请注意,const关键字(仅用于成员实现)通知编译器,比较对象不会修改它们,因此可以允许在 const对象上使用。

Comparing the visible representations

有时最容易得到你想要的比较的方法就是..。

    return lhs.to_string() == rhs.to_string();

... 这通常也是非常昂贵的-这些 string痛苦地创造只是为了扔掉!对于具有浮点值的类型,比较可见表示意味着显示的数字的数量决定了在比较期间将几乎相等的值视为相等的公差。

从 C + + 20开始,通过声明 默认的三路比较运算符(“宇宙飞船”操作符) ,应该可以向类中添加一组完整的默认比较操作符(==<=等) ,如下所示:

struct Point {
int x;
int y;
auto operator<=>(const Point&) const = default;
};

如果使用兼容的 C + + 20编译器,将这一行添加到 Mystruct1和 MyStruct2,假设 Mystruct2的定义是兼容的,那么这一行就足以允许进行相等性比较。