I currently have a class hierarchy like
MatrixBase -> DenseMatrix
-> (other types of matrices)
-> MatrixView -> TransposeView
-> DiagonalView
-> (other specialized views of matrices)
MatrixBase
is an abstract class which forces implementers to define operator()(int,int) and such things; it represents 2 dimensional arrays of numbers. MatrixView
represents a (possibly mutable) way of looking at a matrix, like transposing it or taking a submatrix. The point of MatrixView
is to be able to say something like
Scale(Diagonal(A), 2.0)
where Diagonal
returns a DiagonalView
object which is a kind of lightweight adapter.
Now here's the question(s). I will use a very simple matrix operation as an example. I want to define a function like
template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);
which does the obvious thing the name suggests. I want to be able to pass in either an honest-to-goodness non-view matrix, or an instance of a subclass of MatrixView
. The prototype as written above does not work for statements such as
Scale(Diagonal(A), 2.0);
because the DiagonalView
object returned by Diagonal
is a temporary, and Scale
takes a non-const reference, which cannot accept a temporary. Is there any way to make this work? I tried to use SFINAE, but I don't understand it all that well, and I'm not sure if that would solve the problem. It is important to me that these templated functions can be called without providing an explicit template argument list (I want implicit instantiation). Ideally the statement above could work as written.
Edit: (followup question)
As sbi responded below about rvalue references and temporaries, Is there a way to define two versions of Scale, one which takes a non-const rvalue reference for non-views, and one which takes a pass-by-value view? The problem is to differentiate between these two at compile time in a way such that implicit instantiation will work.
Update
I've changed the class hierarchy to
ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView
The reason WritableMatrixView
is distinct from WritableMatrix
is that the view must be passed around by const reference, while the matrices themselves must be passed around by non-const ref, so the accessor member functions have different const-ness. Now functions like Scale can be defined as
template <class T>
void Scale(const WritableMatrixView<T> &A, const T &scale_factor);
template <class T>
void Scale(WritableMatrix<T> &A, const T &scale_factor){
Scale(WritableMatrixViewAdapter<T>(A), scale_factor);
}
Note that there are two versions, one for a const view, and a non-const version for actual matrices. This means for functions like Mult(A, B, C)
, I will need 8 overloads, but at least it works. What doesn't work, however is using these functions within other functions. You see, each View
-like class contains a member View
of what it's looking at; for example in the expression Diagonal(SubMatrix(A))
, the Diagonal
function returns an object of type DiagonalView<SubMatrixView<T> >
, which needs to know the fully derived type of A
. Now, suppose within Scale
I call some other function like it, which takes either a base view or matrix reference. That would fail because the construction of the needed View
's require the derived type of the argument of Scale; information it does not have. Still working on find a solution to this.
Update
I have used what is effectively a home-grown version of Boost's enable_if to select between two different versions of a function like Scale
. It boils down to labeling all my matrix and view classes with extra typedef tags indicating if they are readable and writable and view or non-view. In the end, I still need 2^N overloads, but now N is only the number of non-const arguments. For the final result, see the here (it's unlikely to get seriously revamped again).