为什么运算符<应该是非成员函数?

Why should operator< be non-member function?

本文关键字:是非 成员 函数 运算符 lt 为什么      更新时间:2023-10-16

我记住C++ Primer,告诉我们operator<应该是non-member function,我总是遵守规则。但现在我想知道原因。

我写了以下代码:

#include <iostream>
using std::cout;
using std::endl;
struct Point1
{
  int x, y;
  Point1(const int a, const int b): x(a), y(b) { }
};
inline bool operator<(const Point1& lhs, const Point1& rhs)
{
  return lhs.x < rhs.x || (lhs.x == rhs.x && lhs.y < rhs.y);
}
struct Point2
{
  int x, y;
  Point2(const int a, const int b): x(a), y(b) { }
  bool operator<(const Point2& rhs)
  {
    return x < rhs.x || (x == rhs.x && y < rhs.y);
  }
};
int main()
{
  Point1 a(1, 2), b(1, 3);
  cout << (a < b) << " " << (b < a) << endl;
  Point2 c(2, 3), d(2, 4);
  cout << (c < d) << " " << (d < c) << endl;
}

在这种情况下,它们似乎没有区别,member函数似乎简单得多。

但在这种情况下:

#include <iostream>
using std::cout;
using std::endl;
 // Usually I write it for comparing floats
class Float1
{
  long double _value;
public:
  static const long double EPS = 1e-8;
  Float1(const long double value): _value(value) { }
  const long double Get() const { return _value; }
};
inline bool operator<(const Float1& lhs, const Float1& rhs)
{
  return rhs.Get() - lhs.Get() > Float1::EPS;
}
inline bool operator<(const Float1& lhs, const long double rhs)
{
  return rhs - lhs.Get() > Float1::EPS;
}
class Float2
{
  long double _value;
public:
  static const long double EPS = 1e-8;
  Float2(const long double value): _value(value) { }
  const long double Get() const { return _value; }
  bool operator<(const Float2& rhs)
  {
    return rhs._value - _value > Float2::EPS;
  }
  bool operator<(const long double rhs)
  {
    return rhs - _value > Float2::EPS;
  }
};
int main()
{
  Float1 x(3.14);
  Float2 y(2.17);
  long double zero = .0;
  cout << (x < zero) << " " << (zero < x) << endl;
  //cout << (y < zero) << " " << (zero < y) << endl; Compile Error!
}

(x<zero)和(zero<x)都有效!(long double是否转换为Float?)

但是(zero<y)不是,因为zero不是Float

您可以看到,在第一种情况下,member function的代码长度较低,而在第二种情况中,non-member function使比较更容易。所以我想知道

  • 在第一种情况下,我应该使用member function而不是non-member function
  • 为什么C++ Primer建议binary operatornon-member function
  • member functionnon-member function是否有其他不同的情况

谢谢你的帮助!

我认为基本答案是非成员函数在隐式转换中发挥得更好。因此,如果您可以将二进制运算符写成非成员函数,那么您应该这样做。

这个问题可以在不同级别得到回答。从设计的角度来看,在最高级别上,operator<是一个二进制运算符。这不再是左手边的操作,而是右手边的操作。另一方面,成员函数绑定到第一个参数,它们是对第一个类型的操作。

从技术角度来看,一直到C++语言,这归结为您已经注意到的:成员函数相对于类型是不对称的。这种缺乏对称性意味着声明为成员函数的operator<只能在左手边是包含该成员的类型的时应用。在调用成员运算符之前,不能应用任何转换。另一方面,由于自由函数与第一个自变量的绑定程度并不比与第二个自变量的约束程度高,因此每当两个自变量中的任何都是适当的类型时,ADL就会提取自由函数operator<,从而允许对第一个和第二个参数进行相同的转换。

作为非成员,比较运算符也适用于派生类(左侧)参数。


EDIT:正如@jonathan在评论中指出的,它还允许值转换,例如int左侧参数。

如果构造函数和转换运算符不是explicit,则这种转换可能允许无意义且可能是无意的代码,例如将T实例与5进行比较。