如何将对子矩阵视图的持久引用作为函数参数传递?

时间:2014-03-28 15:15:01

标签: c++ matrix armadillo

我感兴趣的是将矩阵的子视图传递给其他对象(或函数),以便对象/函数可以修改列/矩阵的已定义段。

我目前正在使用armadillo库,我可以获得并使用对单个矩阵条目的引用(下面的函数f)。我正在寻找一种有效的方法来传递对子视图的引用。我认为下面的函数h的代码实现了这个目标,但我不确定。我的主要问题是:假设我想要一个类存储的引用(不仅仅是在h中的函数中使用),有没有办法改进下面的函数h

三个不太重要的后续问题是:1)在编写函数以接受arma :: Col类型的参数而不是指定子视图时是否可以完成? 2)如果没有,是否有另一个C ++矩阵库允许这种事情而不明确地说明子视图? (我看了Eigen及其.blocks语法,但我认为问题是一样的); 3)我是对的,当表达式完成时,传递给函数g的引用会消失吗?

#include <iostream>
#include <armadillo>

void f(double & x) { x = 5.33392;}
void g(arma::subview_col<double> && x, double y) { x(0) = y; }
void h(arma::subview_col<double>    x, double y) { x(0) = y; }

int main () {
  arma::Mat<double> A = arma::randu<arma::Mat<double> >(3,3);
  A(1,1) = 3;
  A.submat(0,0,1,1) = arma::zeros<arma::Mat<double> >(2,2);
  std::cout << A << std::endl;

  double & k = A(1,1);
  std::cout << k << std::endl;

  f(k);         // THis is ok, works.


  g(A.col(2), 153.0);    // It compiles and the value changes...
  // A.col(2) gets destroyed when the above expression is done
  // evaluating... so not a reference one can keep around.

  std::cout << A << std::endl;

  // Here the subview is passed by value, and it keeps its reference to 
  h(A.col(1), 153.0);

  std::cout << A << std::endl;

  return 0;
}

为我编译:

 g++ -o submatrix_views -std=c++11 ./submatrix_views.cpp

1 个答案:

答案 0 :(得分:2)

我还没有使用犰狳所以我不是最合适的回应,但我认为这是一个更普遍的C ++问题,而且,还没有答案: - )

所以:

  

假设我想要一个类存储的引用(不仅仅是在h中使用函数),有没有办法改进下面的函数h?

我认为你混淆了几个与矩阵和视图无关的基本C ++概念。引用本质上指向一个对象,因此您首先应该担心存储对象本身,然后是引用。

存在对对象的引用这一事实可能是将对象的生命周期延长到引用的生命周期的原因,但情况并非总是如此。有关解释和规则,请参阅this answer之前的一个问题。

因此,确定最佳方法是先存储对象。我敢打赌A.col(1)A.submat(0,0,1,1)是临时对象,您可以通过以下任何方式将它们存储在变量中:

arma::subview_col<double> col = A.col(1);
decltype(A.col(1)) col = A.col(1);
auto col = A.col(1);

如果你知道类型,你可以简单地将一个这样的变量作为一个类的成员(如果你不这样做,只需使用decltype)。我不知道犰狳的详细信息,但我敢打赌,A.col(1)不包含对A的引用,而且包含值为{size_t}的整数(我猜1) {1}}。它是一个轻量级对象,旨在表现为一种通用的引用。不要害怕存放它。只是练习,试试

auto col = A.col(2);
col = 153.0;

任何人都认为它与A.col(2) = 153.0;g(A.col(2), 153.0);相同。此时,没有什么可以阻止您将col存储在任何地方以供以后使用。

将视图存储在某处,然后需要将其传递给函数。如果视图仅用于读取,请写入以下两个重载:

void read(      arma::subview_col<double> && x, double& y) { y = x(0); }
void read(const arma::subview_col<double> &  x, double& y) { y = x(0); }

如果它也用于写作,那么:

void write(arma::subview_col<double> && x, double y) { x(0) = y; }
void write(arma::subview_col<double> &  x, double y) { x(0) = y; }

我推荐的通用解决方案如下:

template <typename X> void read (X&& x, double& y) { y = std::forward<X>(x)(0); }
template <typename X> void write(X&& x, double  y) { std::forward<X>(x)(0) = y; }

或者,甚至更通用:

template <typename X, typename Y>
void read (X&& x, Y&& y) { std::forward<Y>(y) = std::forward<X>(x)(0); }

template <typename X, typename Y>
void write(X&& x, Y&& y) { std::forward<X>(x)(0) = std::forward<Y>(y); }

现在XY可以任何,或者更准确地说,X是任何类型的数组或视图,Y是与X的元素类型兼容(可转换为/来自)的任何类型。如果不是,编译器将对其进行诊断。如果armadillo支持,你甚至可以有一个数组Y和一个数组X的数组(或视图)。

另外,您可以概括为n-ary函数,而无需担心无限const /非 - const重载组合。