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

发布于 2024-11-02 15:11:57 字数 435 浏览 2 评论 0 原文

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

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”(或者 是不可接受的转换)

为什么?

Comparing two instances of the following struct, I receive an error:

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;
};

The error is:

error C2678: binary '==' : no operator
found which takes a left-hand operand
of type 'myproj::MyStruct1' (or there
is no acceptable conversion)

Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

小嗲 2024-11-09 15:11:57

在 C++ 中,struct 默认情况下不生成比较运算符。您需要编写自己的:

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

In C++, structs do not have a comparison operator generated by default. You need to write your own:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}
把人绕傻吧 2024-11-09 15:11:57

C++20 引入了默认比较,又名“太空飞船”运算符<=> ,它允许您请求编译器生成的</<=/==/!=/>=/ 和/或 > 运算符,具有明显/天真的(?)实现......

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

但是你可以针对更复杂的情况进行自定义(下面讨论)。请参阅此处了解语言提案,其中包含理由和讨论。这个答案仍然与 C++17 及更早版本相关,并深入了解何时应该自定义 operator<=> 的实现....

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

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

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

话虽如此,如果 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...

当您“拥有”(即可以编辑企业和第三方库的因素)时对于您想要比较的类,尤其是 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();
}

如果您想要一组更完整的比较运算符,我建议 增强运算符(搜索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 运算符 ==(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_struct2 成员复制到临时成员,然后在比较后将其丢弃。 (当然,您可以通过使构造函数显式或删除an_int的默认值来防止这种隐式构造MyStruct1进行比较。)

成员实现

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

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();

这通常也非常昂贵 - 那些痛苦地创建的字符串只是为了被丢弃!对于具有浮点值的类型,比较可见表示意味着显示的位数决定了比较过程中将几乎相等的值视为相等的容差。

C++20 introduced default comparisons, aka the "spaceship" operator<=>, which allows you to request compiler-generated </<=/==/!=/>=/ and/or > operators with the obvious/naive(?) implementation...

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

...but you can customise that for more complicated situations (discussed below). See here for the language proposal, which contains justifications and discussion. This answer remains relevant for C++17 and earlier, and for insight in to when you should customise the implementation of operator<=>....

It may seem a bit unhelpful of C++ not to have already Standardised this earlier, but often structs/classes have some data members to exclude 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:

  • which fields to compare first, e.g. comparing a particular int member might eliminate 99% of unequal objects very quickly, while a map<string,string> member might often have identical entries and be relatively expensive to compare - if the values are loaded at runtime, the programmer may have insights the compiler can't possibly
  • in comparing strings: case sensitivity, equivalence of whitespace and separators, escaping conventions...
  • precision when comparing floats/doubles
  • whether NaN floating point values should be considered equal
  • comparing pointers or pointed-to-data (and if the latter, how to know how whether the pointers are to arrays and of how many objects/bytes needing comparison)
  • whether order matters when comparing unsorted containers (e.g. vector, list), and if so whether it's ok to sort them in-place before comparing vs. using extra memory to sort temporaries each time a comparison is done
  • how many array elements currently hold valid values that should be compared (is there a size somewhere or a sentinel?)
  • which member of a union to compare
  • normalisation: for example, date types may allow out-of-range day-of-month or month-of-year, or a rational/fraction object may have 6/8ths while another has 3/4ers, which for performance reasons they correct lazily with a separate normalisation step; you may have to decide whether to trigger a normalisation before comparison
  • what to do when weak pointers aren't valid
  • how to handle members and bases that don't implement operator== themselves (but might have compare() or operator< or str() or getters...)
  • what locks must be taken while reading/comparing data that other threads may want to update

So, it's kind of nice to have an error until you've explicitly thought about what comparison should mean for your specific structure, rather than letting it compile but not give you a meaningful result at run-time.

All that said, it'd be good if C++ let you say bool operator==() const = default; when you'd decided a "naive" member-by-member == test was ok. Same for !=. Given multiple members/bases, "default" <, <=, >, and >= implementations seem hopeless though - cascading on the basis of order of declaration's possible but very unlikely to be what's wanted, given conflicting imperatives for member ordering (bases being necessarily before members, grouping by accessibility, construction/destruction before dependent use). To be more widely useful, C++ would need a new data member/base annotation system to guide choices - that would be a great thing to have in the Standard though, ideally coupled with AST-based user-defined code generation... I expect it'll happen one day.

Typical implementation of equality operators

A plausible implementation

It's likely that a reasonable and efficient implementation would be:

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

Note that this needs an operator== for MyStruct2 too.

Implications of this implementation, and alternatives, are discussed under the heading Discussion of specifics of your MyStruct1 below.

A consistent approach to ==, <, > <= etc

It's easy to leverage std::tuple's comparison operators to compare your own class instances - just use std::tie to create tuples of references to fields in the desired order of comparison. Generalising my example from here:

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); }

Then the comparisons above simplify to:

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

If you want a fuller set of comparison operators, I suggest boost operators (search for less_than_comparable). If it's unsuitable for some reason, you may or may not like the idea of support macros (online):

#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)

...that can then be used a la...

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

(C++14 member-tie version here)

Discussion of specifics of your MyStruct1

There are implications to the choice to provide a free-standing versus member operator==()...

Freestanding implementation

You have an interesting decision to make. As your class can be implicitly constructed from a MyStruct2, a free-standing / non-member bool operator==(const MyStruct2& lhs, const MyStruct2& rhs) function would support...

my_MyStruct2 == my_MyStruct1

...by first creating a temporary MyStruct1 from my_myStruct2, then doing the comparison. This would definitely leave MyStruct1::an_int set to the constructor's default parameter value of -1. Depending on whether you include an_int comparison in the implementation of your operator==, a MyStruct1 might or might not compare equal to a MyStruct2 that itself compares equal to the MyStruct1's my_struct_2 member! Further, creating a temporary MyStruct1 can be a very inefficient operation, as it involves copying the existing my_struct2 member to a temporary, only to throw it away after the comparison. (Of course, you could prevent this implicit construction of MyStruct1s for comparison by making that constructor explicit or removing the default value for an_int.)

Member implementation

If you want to avoid implicit construction of a MyStruct1 from a MyStruct2, make the comparison operator a member function:

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

Note the const keyword - only needed for the member implementation - advises the compiler that comparing objects doesn't modify them, so can be allowed on const objects.

Comparing the visible representations

Sometimes the easiest way to get the kind of comparison you want can be...

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

...which is often very expensive too - those strings painfully created just to be thrown away! For types with floating point values, comparing visible representations means the number of displayed digits determines the tolerance within which nearly-equal values are treated as equal during comparison.

晚雾 2024-11-09 15:11:57

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

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

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

You need to explicitly define operator == for MyStruct1.

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.

虚拟世界 2024-11-09 15:11:57

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

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

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

Starting in C++20, it should be possible to add a full set of default comparison operators (==, <=, etc.) to a class by declaring a default three-way comparison operator ("spaceship" operator), like this:

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

With a compliant C++20 compiler, adding that line to MyStruct1 and MyStruct2 may be enough to allow equality comparisons, assuming the definition of MyStruct2 is compatible.

未央 2024-11-09 15:11:57

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

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

By default structs do not have a == operator. You'll have to write your own implementation:

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }
一抹淡然 2024-11-09 15:11:57

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

Out of the box, the == operator only works for primitives. To get your code to work, you need to overload the == operator for your struct.

猫弦 2024-11-09 15:11:57

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

Because you did not write a comparison operator for your struct. The compiler does not generate it for you, so if you want comparison, you have to write it yourself.

孤蝉 2024-11-09 15:11:57

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

Comparison doesn't work on structs in C or C++. Compare by fields instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文