使用模板 类 在 C++ 中重载运算符

Overloading operators in C++ with template classes

我有以下模板class:

    template <class T>
class Matrix {

    public:
        Matrix(size_t rows, size_t columns, const T elements = 0);



        // scalar multiplication
        Matrix<T> operator*(const T& rhs){

            Matrix<T> result(rows, columns);

            for(size_t index = 0; index < rows * columns; ++index){
                result.elements[index] = elements[index] * rhs;
            }

            return result;
        }

        Matrix <T> operator*(const T& lhs, const Matrix<T>& rhs);



        const size_t rows;
        const size_t columns;


        private:

            std::vector<T> elements;
};

以及运算符*的以下实现:

// scalar multiplication
template <class T>
Matrix<T> Matrix<T>::operator*(const T& lhs, const Matrix<T>& rhs){

    Matrix<T> result(rhs.rows, rhs.columns);

    for(size_t index = 0; index < rhs.rows * rhs.columns; ++index){
        result.elements[index] = elements[index] * lhs;
    }
    return result;
}

当我尝试编译 clang 时说:error: overloaded 'operator*' must be a unary or binary operator (has 3 parameters)|

而且我不太明白,我在这方面缺少什么。总的来说,模板 classes 在重载运算符时给我带来了困难,我不知道为什么。 SO 上有一些关于此主题的帖子,我尝试了一些代码变体,但 none 的代码有效。

您将 Matrix <T> operator*(const T& lhs, const Matrix<T>& rhs); 声明为成员函数,它有一个隐式参数 this,这就是编译器抱怨它的原因 "has 3 parameters"。

你可以把它做成一个免费的模板函数,

template <class T>
class Matrix {
    ...
    template <class Z>
    friend Matrix<Z> operator*(const Z& lhs, const Matrix<Z>& rhs);
    ...
};

// scalar multiplication
template <class Z>
Matrix<Z> operator*(const Z& lhs, const Matrix<Z>& rhs){

    Matrix<Z> result(rhs.rows, rhs.columns);

    for(size_t index = 0; index < rhs.rows * rhs.columns; ++index){
        result.elements[index] = elements[index] * lhs;
    }
    return result;
}

你的函数是一个成员函数。成员函数有一个隐藏参数,即 this 指针。

您要么需要使您的 operator* 成为非成员函数,要么您需要删除 operator* 函数的参数之一(然后它将 "this" 中的数据与数据相乘在传入矩阵<T>.)

解决这个问题的简单有效的方法如下:

  1. 先执行Matrix& operator *=(SomeType const&)等类似操作。这些 变异 操作会更改 class 的实例,然后 return 对 *this.

    [=62= 的引用]
  2. 根据 *= 将其他操作作为内联朋友实现,其中 lhs(通常)参数按值获取、修改和 returned。

这往往非常简单,而且通常比从 operator* 而不是 operator*= 开始更有效。

所以你将拥有:

 template<class T, etc>
 struct Matrix{
   Matrix& operator*=(T const&);
   Matrix& operator*=(Matrix const&);
   Matrix& operator+=(Matrix const&);

您传统上实现的。那么:

   friend Matrix operator*(T const& t, Matrix m){ m*=t; return m; }
   friend Matrix operator*(Matrix m, T const& t){ m*=t; return m; }

   friend Matrix operator*(Matrix lhs, Matrix const& rhs){ lhs*=rhs; return lhs; }
   friend Matrix operator+(Matrix lhs, Matrix const& rhs){ lhs+=rhs; return lhs; }

现在您只需要实施一些传统的方法。

这些 friend 运算符是为 Matrix 的每个模板实例自动生成的非模板内联非方法函数。

您的直接问题是您的非静态运算符除了其两个显式参数外实际上还采用隐式 this,而二元运算符不能采用 3 个参数。


复杂且更有效的解决方案涉及一种通常称为 "expression templates" 的技术。缺点是表达式模板编写起来更复杂,并且在 auto 关键字和类似情况下有一些脆弱点。

举个例子:

Matrix m = m1 * m2 + m3 * m4 + m5 + m6;

一个表达式模板将完成上述任务,只需分配一次矩阵的内部数据。

我上面的代码将复制 m1,将结果乘以 m2。然后它将复制 m3,然后将其乘以 m4。然后它会把所有的东西加起来而不做任何额外的副本。最后,这个结果会被移入m.

因此在表达式模板情况下将创建两个矩阵而不是 1 个矩阵。

更天真的解决方案(如 OP 的设计)将创建 5 个矩阵而不是 2 个。