115

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

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”类型的左侧操作数的运算符(或没有可接受的转换)

为什么?

4

8 回答 8

150

在 C++ 中,structs 默认没有生成比较运算符。你需要自己写:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}
于 2011-04-21T06:25:17.530 回答
114

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

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

...但是您可以针对更复杂的情况进行自定义(如下所述)。有关语言建议,请参见此处,其中包含理由和讨论。这个答案仍然与 C++17 及更早版本相关,并且有助于深入了解何时应该自定义operator<=>....

C++ 之前没有标准化这似乎有点无益,但通常结构/类有一些数据成员要从比较中排除(例如计数器、缓存结果、容器容量、最后操作成功/错误代码、游标),如以及对无数事情做出的决定,包括但不限于:

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

因此,在您明确考虑比较对您的特定结构意味着什么之前,最好有一个错误,而不是让它编译但在运行时没有给您一个有意义的结果

综上所述,如果 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;
}

请注意,这也需要一个operator==for MyStruct2

此实现的含义和替代方案将在下面的“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...

当您“拥有”(即可以编辑具有公司和第三方库的因素)您想要比较的类时,尤其是在 C++14 准备从return语句中推断函数返回类型的情况下,添加“ tie" 成员函数与您希望能够比较的类:

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

然后上面的比较简化为:

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

如果您想要更完整的比较运算符集,我建议使用boost 运算符(搜索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==()……

独立实施

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

my_MyStruct2 == my_MyStruct1

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

会员实施

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

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

注意const关键字 - 只需要成员实现 - 建议编译器比较对象不会修改它们,因此可以在const对象上允许。

比较可见表示

有时,获得所需比较的最简单方法可能是......

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

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

于 2011-04-21T06:43:15.377 回答
16

您需要明确定义operator ==for MyStruct1

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

现在 == 比较对于 2 个这样的对象是合法的。

于 2011-04-21T06:25:42.633 回答
12

从 C++20 开始,应该可以通过声明默认的三向比较运算符(“spaceship”运算符)向类中添加一整套默认比较运算符(==、、等),如下所示:<=

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

使用兼容的 C++20 编译器,假设 MyStruct2 的定义是兼容的,将该行添加到 MyStruct1 和 MyStruct2 可能足以允许相等比较。

于 2019-03-27T20:35:49.753 回答
2

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

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }
于 2011-04-21T06:42:19.760 回答
1

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

于 2011-04-21T06:25:10.440 回答
0

开箱即用,== 运算符仅适用于原语。为了让您的代码正常工作,您需要为您的结构重载 == 运算符。

于 2011-04-21T06:25:21.510 回答
0

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

于 2011-04-21T06:25:54.880 回答