【问题标题】:Overloading operators in C++ with template classes使用模板类重载 C++ 中的运算符
【发布时间】:2016-06-26 22:26:46
【问题描述】:

我有以下模板类:

    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;
};

以及 operator* 的以下实现:

// 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)|

我不太明白,我缺少什么。一般来说,模板类在重载运算符时给我带来了困难,我不知道为什么。这里有一些关于这个主题的帖子,我尝试了一些代码的变体,但都没有奏效。

【问题讨论】:

  • 作为成员函数,它隐式地将this 作为第一个参数,但您想再添加 2 个。假设您有Matrix a,b,c;,您的运营商将被称为a.operator*(b,c),这有意义吗?没有;)

标签: c++ class templates overloading operator-keyword


【解决方案1】:

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

  1. 首先实现Matrix&amp; operator *=(SomeType const&amp;) 和类似的操作。这些是 mutating 操作,它们更改类的实例,然后返回对 *this 的引用。

  2. 根据 *= 将其他操作作为内联朋友实现,其中 lhs(通常)参数按值获取、修改并返回。

这往往比以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 个参数。


复杂且更有效的解决方案涉及一种通常称为“表达式模板”的技术。缺点是表达式模板写起来比较复杂,在auto关键字和类似情况周围有几个脆弱点。

举个例子:

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

表达式模板将仅使用矩阵内部数据的一次分配来完成上述操作。

我上面的代码将复制m1,将结果乘以m2。然后它将复制m3,然后乘以m4。然后它将添加所有内容而不制作任何额外的副本。最后,这个结果会被移动到m

因此将创建两个矩阵,而不是表达式模板案例中的 1 个。

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

【讨论】:

  • 谢谢!很多时候我不确定是否应该在类定义内或之外实现一个函数。有一般规则吗?
  • @kimsay 如果它很短并且您对inline 及其危险没意见,请在类定义中进行。如果不是,把它放在其他地方?对于上述 Koenig 友元运算符,您必须在类定义中进行(因为无法在类之外引用运算符):这也意味着(对我而言)它们应该总是很短。
  • 朋友也只能在那里访问私有字段吗?
  • @kimsay nope:它做了很多事情。它使 ADL 工作顺利,使运算符本身非模板(它有一些很好的特性:模板运算符可能很脆弱),它使它们成为非成员二元运算符等。
  • 抱歉,知道了!谢谢! @牦牛
【解决方案2】:

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

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

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;
}

【讨论】:

  • 朋友之前试过了,还是不行,@songyuanyao
  • @kimsay 我的错。固定。
  • 感谢@songyuanyao。我是否只需要朋友来访问私有字段,还是还有更多?
  • @kimsay 仅用于访问私有字段,仅此而已。
【解决方案3】:

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

你要么需要让你的 operator* 成为一个非成员函数,要么你需要去掉你的 operator* 函数的一个参数(然后它将“this”中的数据与传入矩阵中的数据相乘&lt;T&gt;.)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多