重载 * 乘法运算符两次作为成员函数?

Overloading * multiplication operator twice as a member function?

向下滚动 TL:DR。

此题与this题相似,但有一些不同。它涉及为名为 jVector 的 class 重载 * 运算符两次,它仅表示二维笛卡尔向量。

第一种乘法是jVector * jVector,或dot product。第二种是乘以实数,double * jVector。这只是 return 一个向量,其条目乘以双精度。

下面是一些代码来说明我正在尝试做的事情:

class jVector{
    public:
        double x, y;

        jVector(double x_val = 0., double y_val = 0.){
            x = x_val;
            y = y_val;
        }

        //Operator overload functions
        //[...]

        //F1: Dot product (WORKS)
        double operator* (jVector& factor){
            double result;
            result = x * factor.x;
            result += y * factor.y;
            return result;
        }

        //F2: Real number multiplication (DOES NOT WORK)
        jVector operator* (double f){
            jVector result;
            result.x = x * f;
            result.y = y * f;
            return result;
        }

        //[...]
}

//F3: As a non-member (WORKS)
jVector operator* (double f, jVector V){
    jVector result;
    result.x = V.x * f;
    result.y = V.y * f;
    return result;
}

三个相关函数被标记为F1F2F3。函数 F2F3 永远不会同时定义(我注释掉其中一个以测试另一个)。

这是尝试表达类似 2.0 * Foo 的结果,其中 Foo 是类型 jVector 的向量。当使用 F3 时,该操作按预期工作,该函数定义在 class 之外。但是,当仅使用成员函数 F2 时,会出现错误 no match for 'operator*' in '2 * Foo'.

如果您根本不重载运算符,这与您遇到的错误类型相同,表明我没有正确定义 F2,或者 F2F1.

我相当确定我的问题与之前 question I mentioned 中的问题不同,因为 F1F2 具有不同的 return 类型 参数类型。

TL:DR

所以这是我的问题:为什么我能够重载 * 两次,只要其中一个被定义为非成员函数?为什么两个重载函数不能都是 class 的成员?

对于成员函数运算符重载,第一个操作数 必须是class 的对象。重载函数的参数是第二个操作数。所以:

double operator* (double f){

仅适用于您正在执行 a_vector * a_double 的情况,不适用于 a_double * a_vector

出于这个原因(和其他原因),对重载运算符使用非成员函数通常更好。我建议这样做的方式是:

// member function
jVector & jVector::operator*=( double f )
{
    x *= f;
    y *= f;
    return *this;
}

// free functions
jVector operator* (double f, jVector V) { return V *= f; }
jVector operator* (jVector V, double f) { return V *= f; }

它不能用作成员函数,因为您的 F2 替代方案始终将 jVector 作为第一个操作数(它是一个成员函数,因此您无法选择第一个参数是什么是 - 它是 jVector *this [被语言隐藏])。理论上,编译器可以允许两个操作数交换位置(它可以将 x * 2.0 转换为 2.0 * x 以进行常规数学运算,但由于运算符重载不是 "swapped around",因此如果编译器 DID 重新安排它们就不会很好)

要使第一个操作数成为 double,您需要一个独立函数。