我读到一个声明为成员函数的重载运算符是不对称的,因为它只能有一个参数,而另一个自动传递的参数是this指针。因此,没有标准可以比较它们。另一方面,声明为friend的重载运算符是对称的,因为我们传递了两个相同类型的参数,因此可以将它们进行比较。作为参考,为什么朋友是首选? (使用非对称版本可以获得与对称版本相同的结果)
为什么STL算法仅使用对称版本?

评论

您的问题实际上仅涉及二进制运算符。并非所有重载运算符都限于单个参数。 ()运算符可以采用任意数量的参数。另一方面,一元运算符不能有任何参数。

stackoverflow.com/a/4421729/103167

这是C ++常见问题解答中涉及的众多主题之一:运算符重载

#1 楼

如果将运算符重载函数定义为成员函数,则编译器会将s1 + s2之类的表达式转换为s1.operator+(s2)。这意味着,运算符重载的成员函数在第一个操作数上被调用。那就是成员函数的工作方式!

但是如果第一个操作数不是一个类怎么办?如果要重载第一个操作数不是类类型的运算符,则存在一个主要问题,请说double。所以你不能这样写10.0 + s2。但是,您可以为s1 + 10.0之类的表达式编写运算符重载成员函数。为了解决此排序问题,如果需要访问friend成员,我们将运算符重载函数定义为private。仅在需要访问私有成员时才使其成为friend。否则,只需使其成为非朋友非成员函数即可改善封装!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}


阅读这些:操作数排序的一个小问题非成员函数如何改善封装

评论


“只有当它需要访问私有成员时,并且当您对编写访问器感到厌倦时,才将它当作朋友吧?

– Badmaash
2011年1月7日,下午5:15

@Abhi:选择:改进封装与惰性书写习惯!

–纳瓦兹
2011年1月7日在5:17



@matthias,并非所有运算符都是可交换的。一个简单的例子是a / b。

–edA-qa mort-ora-y
2012年4月25日在7:31

避免让您的非成员操作员成为朋友的一种常见方法是根据操作分配操作员(几乎肯定是公共成员)来实施它们。例如,您可以将T T :: operator + =(const T&rhs)定义为成员,然后将非成员T运算符(T lhs,const T&rhs)定义为return lhs + = rhs;。非成员函数应在与类相同的名称空间中定义。

–阿德里安·麦卡锡(Adrian McCarthy)
16年8月10日在18:58

@ricky:但是如果lhs是副本(如我的评论所示),那么lhs更改的事实并不重要。

–阿德里安·麦卡锡(Adrian McCarthy)
18年11月19日在21:57

#2 楼

friend运算符重载和成员函数运算符重载之间并不一定要区分,因为它是全局运算符重载和成员函数运算符重载之间的区别。

首选全局运算符重载的一个原因是是否要允许表达式类类型出现在二进制运算符右侧的位置。例如:

Foo f = 100;
int x = 10;
cout << x + f;


仅当


Foo运算符+(int x, const Foo&f);


请注意,全局运算符重载不一定是friend函数。这仅在需要访问Foo的私有成员时才是必需的,但并非总是如此。

如果Foo仅具有成员函数运算符重载,例如:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};


...那么,我们只能有一个表达式,其中Foo实例出现在加号运算符的左侧。

评论


+1用于区分成员函数和非成员函数,而不是成员函数和朋友函数。我想今天我们会说“全局或命名空间范围”。

–阿德里安·麦卡锡(Adrian McCarthy)
16年8月10日在18:46