C ++中的运算符+ for matrices

时间:2010-03-17 16:47:31

标签: c++ operators matrix

我认为矩阵的+运算符是天真的实现(例如2D) 在C ++中将是:

class Matrix {

  Matrix operator+ (const Matrix & other) const {
      Matrix result;
      // fill result with *this.data plus other.data
      return result;
  }
}

所以我们可以像

一样使用它
Matrix a;
Matrix b;
Matrix c;

c = a + b;

右?

但是如果矩阵很大,那么效率就不高,因为我们正在做一个不必要的复制(返回结果)。

因此,如果我们不高效,我们必须忘记干净的电话:

c = a + b;

右?

你会建议/更喜欢什么? 感谢。

8 个答案:

答案 0 :(得分:12)

C ++标准允许编译器在这种情况下忽略不必要的副本(它被称为“命名返回值优化”,通常缩写为NRVO)。当你返回一个临时变量而不是一个命名变量时,会有一个匹配的“RVO”。

几乎所有合理的C ++编译器都实现了NRVO和RVO,所以一般来说你可以忽略这个结构不会特别有效的事实。

编辑:我当然是在谈论返回新矩阵所涉及的副本,其中包含了添加的结果。您可能想要通过const引用传递输入:

Matrix operator+(Matrix const &other) const { 
    Matrix result;
    // ...
    return result;
}

...或者,按值传递,但返回传递的值:

Matrix operator+(Matrix other) const { 
    other += *this;
    return other;
}

请注意,这取决于交换性(即,它实际上是b+a而不是a+b),所以虽然它适用于添加,但它不适用于其他一些操作。

答案 1 :(得分:3)

您可以在不触发复制构造函数的情况下返回值。被称为RValue引用 这里详细解释http://www.artima.com/cppsource/rvalue.html

答案 2 :(得分:2)

请注意,您的第一个天真实现非常原生,因为没有任何内容通过引用传递。我认为这是一个非常天真的例子,并且没有必要提醒读者通过引用而不是按值传递的好处。

也请注意,我使用的是非成员函数运算符,而不是成员函数,但最后,结果(几乎)相同。

如果您想确保不会创建任何必要的副本,您应该尝试非操作员版本:

void add(Matrix & result, const Matrix & lhs, const Matrix & rhs) ;

如果你想以操作方式(这是我首选的解决方案)来做,那么你应该假设operator +将创建一个临时的。然后,您应该定义operator +和operator + =:

Matrix & operator += (Matrix & result, const Matrix & rhs) ;
{
   // add rhs to result, and return result
   return result ;
}

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

现在,您可以尝试“利用”编译器优化并将其写为:

Matrix & operator += (Matrix & result, const Matrix & rhs) ;
{
   // add rhs to result, and return result
   return result ;
}

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

由Herb Sutter在 C ++编码标准中提出, 27。首选算术和赋值运算符的规范形式,p48-49:

  

变体是让运算符@ [@是+, - ,无论如何]按值接受其第一个参数。这样,您可以安排编译器本身为您执行复制,这可以让编译器在应用优化时有更多的余地。

答案 3 :(得分:1)

正如其他人所指出的那样,您的实施并不像您想象的那么昂贵。但是,定义用于在关键内部循环中使用的就地修改对象的其他方法可能是有意义的。

编辑 - 修正了以下段落

这里的要点是,即使使用返回值优化,您仍然最终构造一个局部变量,然后在operator +退出后将其分配给结果变量。当然,要破坏那个额外的对象。还有一个额外的对象用于临时保存结果。可以使用copy-on-write进行引用计数,但这会为每次使用矩阵添加解除引用开销。

对于99%的案例,这些都不是问题,但偶尔会遇到一个关键案例。如果您正在处理大型矩阵,那么引用计数开销将是微不足道的 - 但对于2D到4D的2D,有时您可能会关注那些额外的周期 - 或者更多关于不是< / em>当你想要它在堆栈上或嵌入在某个struct / class / array中时,将矩阵放在堆上。

那就是说 - 在这些情况下,你可能不会编写自己的矩阵代码 - 你只需要使用DirectX或OpenGL中的矩阵运算等。

答案 4 :(得分:1)

如果你真的关心性能(你有没有分析?),你可能根本不应该实现operator +,因为你无法控制它是否会导致创建一个非最佳的临时性。只需实现operator + =和/或成员函数add(Matrix& result, const Matrix& in1, const Matrix& in2),让您的客户创建正确的临时工具。

如果你想要操作员+ Jerry Coffin中的任何一个都能正常工作。

答案 5 :(得分:0)

解决它的两种方法。

1)使用参考文献:

Matrix& operator+ (Matrix& other) const {

2)在复制构造函数中使用浅拷贝。是的,将创建新的Matrix对象,但不会再创建实际矩阵

答案 6 :(得分:0)

我会尝试在return语句中构造Matrix:

Matrix Matrix::operator + (const Matrix& M)
{
    return Matrix(
        // 16 parameters defining the 4x4 matrix here
        // e.g. m00 + M.m00, m01 + M.m01, ...
    );
}

这样你就不会使用任何临时工。

答案 7 :(得分:0)

class Matrix { 

  Matrix & operator+=(const Matrix & other) {
      // fill result with *this.data plus other.data 
      // elements += other elements 
      return *this;
  }
  Matrix operator+ (const Matrix & other) {
      Matrix result = *this; 
      return result += other; 
  } 
}