重载相同操作的友元函数和 class 方法的优先级

Priority of friend function and class method that overload the same operation

这是我的代码:

#include <iostream>
using namespace std;

class RationalNumber
{
private:
    int num;
    int den;
public:

    RationalNumber(int a):num(a),den(1){}

    RationalNumber(int a, int b):num(a),den(b){}

    void parse()
    {
        cout << num << "\" << den << endl;
    }
    
    RationalNumber operator/(RationalNumber);
};

RationalNumber  RationalNumber:: operator/(RationalNumber b)
    {
        return RationalNumber(num * b.den, den * b.num);
    }

int main()
{
    RationalNumber a(1,2);
    int c = 5;
    (a/c).parse();
    return 0;
}

我有一个方法可以为“RationalNumber”的 class 和 returns 对应对象重载“/”操作。

我还有一个构造函数,允许我用整型变量初始化我的对象。也就是说,我可以对 RationalNumber 对象和整数变量使用我重载的“/”操作,所以我的程序的结果是 1/10。

但后来我在我的代码中添加了几行:

#include <iostream>
using namespace std;

class RationalNumber
{
private:
    int num;
    int den;
public:

    RationalNumber(int a):num(a),den(1){}

    RationalNumber(int a, int b):num(a),den(b){}

    void parse()
    {
        cout << num << "\" << den << endl;
    }
    
    RationalNumber operator/(RationalNumber);
    friend RationalNumber operator/(RationalNumber, int);
};

RationalNumber  RationalNumber:: operator/(RationalNumber b)
    {
        return RationalNumber(num * b.den, den * b.num);
    }

RationalNumber operator/(RationalNumber v, int n )
    {
        return RationalNumber(0,0);
    }
int main()
{
    RationalNumber a(1,2);
    int c = 5;
    (a/c).parse();
    return 0;
}

我添加了一个友元函数,它也重载“/”操作,接收“RationalNumber”对象和整数,以及returns 0/0 - “RationalNumber”对象。

所以现在 c 在表达式 a/c 可以被视为我的朋友函数中的第二个参数 - 或者被我的构造函数之一用整数值初始化的“RationalNumber”对象。 现在程序的结果是 0/0——也就是说,编译器引用了友元函数,而不是方法,我想知道为什么。这种情况有明确的优先级吗?

我将展示一个简化的视图。为此,您将扮演编译器的角色。

假设您正忙于编译一些代码并遇到一个运算符,该运算符被赋予 RationalNumberint 作为操作数。该怎么办?好吧,编译器的工作描述包括将运算符与实现相匹配,因此您抱怨并查看您的声明列表。您找到了两个可行的选择。

  1. RationalNumber 的一个成员,期望 RationalNumber 作为它的第二个操作数。这不是一个完美的匹配,但是您 可以 找到您记得在某处看到的从 intRationalNumber 的令人讨厌的转换。
  2. 一个自由运算符,期望 RationalNumber 作为第一个操作数,int 作为第二个操作数。绝配!

您正在忙于编译代码。您真的不想找到那个转换,所以您选择 #2 作为更简单的选项。

现实并没有上面那么轻率,但是背后的道理是健全的。在编译了一个 viable functions, the compiler will look for the best match between declared parameters and supplied arguments. Loosely speaking, the "best" match is the one that requires the least work by the compiler. (There is a ranking 的转换列表之后,使这个变得精确。)完全匹配——提供的参数已经是需要的类型——比其他任何东西都更受欢迎。

此时在decision-making过程中,没有考虑“朋友”和“成员”之间的区别。