在比较C++中的结构时找不到==运算符

Jon*_*han 91 c++ struct comparison-operators

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

struct MyStruct1 {
    Position(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;
};
Run Code Online (Sandbox Code Playgroud)

错误是:

错误C2678:二进制'==':找不到哪个运算符带有'myproj :: MyStruct1'类型的左手操作数(或者没有可接受的转换)

为什么?

Ant*_*ams 121

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

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

  • @Jonathan:为什么C++会知道如何比较你的`struct`s是否相等?如果你想要简单的方法,那么总是`memcmp`你的结构不包含指针. (21认同)
  • @Jonathan我所知道的"现代"语言确实提供了一个`=='运算符---语义几乎不是想要的.(并且它们不提供覆盖它的方法,因此您最终必须使用成员函数).我所知道的"现代"语言也没有提供值语义,因此即使它们不合适,你也不得不使用指针. (16认同)
  • @Xeo:`memcmp`与非POD成员(如`std :: string`)和填充结构失败. (9认同)
  • 至少它_should_可能'=默认'它! (5认同)
  • @Jonathan案件肯定会有所不同,即使在给定的程序中也是如此.对于实体对象,Java提供的解决方案非常有效(当然,你可以在C++中做同样的事情 - 它甚至是实体对象的惯用C++).问题是如何处理价值对象.出于C兼容性的原因,C++提供了一个默认的`operator =`(即使它经常做错了).但是,C兼容性不需要`operator ==`.在全球范围内,我更喜欢C++对Java的作用.(我不知道C#,所以也许这样更好.) (4认同)
  • @James - 我们的用例可能不同:) (2认同)
  • 由于在 C++ 中 `struct` 是一个类,你可以将 `bool operator==(const MyStruct1& other)` 定义和声明添加到你的 `struct` 中,然后使用它。 (2认同)

Ton*_*roy 84

正如其他人所说,你需要自己实现一个比较功能.

有一种建议的方法要求编译器生成明显的/幼稚的(?)实现:请参阅此处.

这似乎有点无益的C++不是已经规范这一点,但往往结构/类有一些数据成员排除从比较(如柜台,缓存的结果,容器容量,最后一次操作成功/错误代码,光标),以及作为做出无数事情的决定,包括但不限于:

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

所以,在你明确考虑比较对于你的特定结构应该意味着什么,而不是让它编译但在运行时没有给你一个有意义的结果之前,有一个错误很好的.

所有这一切,如果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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这需要一个operator==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...
Run Code Online (Sandbox Code Playgroud)

当你"拥有"(即可以编辑公司和第三方库的一个因素)你要比较的类时,特别是C++ 14准备从return语句中推断出函数返回类型时,通常可以更好地添加"将"成员函数绑定到您希望能够比较的类:

auto tie() const { return std::tie(my_struct1, an_int); }
Run Code Online (Sandbox Code Playgroud)

然后上面的比较简化为:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.tie() == rhs.tie();
}
Run Code Online (Sandbox Code Playgroud)

如果你想要一套更完整的比较运算符,我建议使用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)
Run Code Online (Sandbox Code Playgroud)

......那可以用来......

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
Run Code Online (Sandbox Code Playgroud)

(C++ 14构件结版本这里)

讨论MyStruct1的细节

选择提供一个独立的成员与成员有关operator==()...

独立实施

你有一个有趣的决定.由于你的课程可以从a隐式构建MyStruct2,独立/非成员bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)函数将支持...

my_MyStruct2 == my_MyStruct1
Run Code Online (Sandbox Code Playgroud)

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

会员实施

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

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

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

比较可见的表示

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

    return lhs.to_string() == rhs.to_string();
Run Code Online (Sandbox Code Playgroud)

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


iam*_*ind 16

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

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

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


Joe*_*Lee 12

在C ++ 20日起,它应该是可能的全套默认比较操作(添加==<=通过声明等)一类的默认三路比较操作符(“宇宙飞船”运营商),就像这样:

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};
Run Code Online (Sandbox Code Playgroud)

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