首页 > 解决方案 > C++ 比较运算符重载 const 与非 const 行为

问题描述

我最近注意到一些我自己无法弄清楚的运算符重载行为。以下两个类仅在const的成员比较运算符重载上有所不同ClassAClassB它们不是常量。一般来说,我知道人们总是更喜欢那个const,但我仍然对为什么我们看到我将在下面描述的行为感兴趣。

#include <string>

class ClassA {
public:
    explicit ClassA(double t) : _t(t) {}

    std::string operator<=(int const& other) const {
        return "A(<=)";
    }

    std::string operator==(int const& other) const {
        return "A(==)";
    }

    friend std::string operator<=(int const& other, ClassA const& expr) {
        return "A'(<=)";
    }

    friend std::string operator==(int const& other, ClassA const& expr) {
        return "A'(==)";
    }

private:
    double _t;
};

class ClassB {
public:
    explicit ClassB(double t) : _t(t) {}

    std::string operator<=(int const& other) {
        return "B(<=)";
    }

    std::string operator==(int const& other) {
        return "B(==)";
    }

    friend std::string operator<=(int const& other, ClassB const& expr) {
        return "B'(<=)";
    }

    friend std::string operator==(int const& other, ClassB const& expr) {
        return "B'(==)";
    }

private:
    double _t;
};

现在我想在const非常量场景中使用这些类和比较函数。

int
main(int argc,
     char* argv[]) {
    ClassA a1{0};
    1==a1; //OK
    1<=a1; //OK
    ClassA const a2{0};
    1==a2; //OK
    1<=a2; //OK
    ClassB b1{0};
    1==b1; //NOT OK
    1<=b1; //OK
    ClassB const b2{0};
    1==b2; //OK
    1<=b2; //OK

    return 0;
}

一切正常,但我标记的那一行NOT OK。这会引发编译器错误。

error C2446: '==': no conversion from 'ClassB' to 'int'

我的问题分为三个部分,但我希望有一个很好的理由可以回答所有问题。因此,我希望将其发布到一个单一的 SO 问题中仍然可以。

当不等式存在时,为什么不等式运算符==不编译<=?为什么成员函数是否存在对友元函数很重要const?为什么要让ClassB对象const修复它?

更新:

  1. 改写的候选人:
  • 对于四个关系运算符表达式 x<y、x<=y、x>y 和 x>=y,找到的所有成员、非成员和内置运算符<=> 都将添加到集合中。
  • 对于四个关系运算符表达式 x<y, x<=y, x>y, and x>=y 以及三路比较表达式 x<=>y,两个参数顺序颠倒的合成候选为找到的每个成员、非成员和内置运算符<=> 添加。
  • 对于 x!=y,所有找到的成员、非成员和内置运算符 == 都将添加到集合中。
  • 对于相等运算符表达式 x==y 和 x!=y,为找到的每个成员、非成员和内置运算符 == 添加一个合成候选,其中两个参数的顺序颠倒。

在所有情况下,都不会在重写表达式的上下文中考虑重写的候选。对于所有其他运算符,重写的候选集为空。

error: return type 'std::string' (aka 'basic_string<char>') of selected 'operator==' function for rewritten '==' comparison is not 'bool'
    1==b1; //NOT OK

海合会

error: return type of 'std::string ClassB::operator==(const int&)' is not 'bool'
    1==b1; //NOT OK

虽然这些都是非常有趣的见解,但最初的问题仍然悬而未决。

标签: c++compiler-errorsoperator-overloadinglanguage-lawyerc++20

解决方案


不是一个具体的答案。但是,让我们看看文档

重载运算符的返回类型没有限制(因为返回类型不参与重载决议),但有规范的实现

...该语言对重载运算符的作用或返回类型没有其他限制,但一般来说,重载运算符的行为应与内置运算符尽可能相似

接着:

..返回类型受到预期使用运算符的表达式的限制

例如,赋值运算符通过引用返回,从而可以写出 a = b = c = d,因为内置运算符允许这样做。

我们进一步挖掘:

...在内置运算符返回 bool 的情况下,大多数用户定义的重载也返回 bool,因此用户定义的运算符可以以与内置运算符相同的方式使用。但是,在用户定义的运算符重载中,任何类型都可以用作返回类型(包括 void)。

更进一步(三向比较

如果两个操作数都具有算术类型,或者如果一个操作数具有无作用域枚举类型而另一个具有整数类型,则通常的算术转换将应用于操作数。

所以,我会断言它取决于 implementation。在我的机器上它编译(g++)并运行:

std::cout << (1==b1) << std::endl; // Prints B'(==)

微小的重新更新

@463035818_is_not_a_number“在 VS 中发现了这个问题。新版本的 gcc 也拒绝这种用法,与 clang 相同。看起来它是一个错误/缺失的功能,并在更新的版本中得到了修复。”

这是有问题的编译器资源管理器片段


推荐阅读