I actualmente tiene una jerarquía de clases comoC++ implícita de instancias de plantilla
MatrixBase -> DenseMatrix
-> (other types of matrices)
-> MatrixView -> TransposeView
-> DiagonalView
-> (other specialized views of matrices)
MatrixBase
es una clase abstracta que obliga a los ejecutores para definir operador() (int, int) y tales cosas; representa 2 matrices dimensionales de números. MatrixView
representa una forma (posiblemente mutable) de mirar una matriz, como transponerla o tomar una submatriz. El punto de MatrixView
es ser capaz de decir algo como
Scale(Diagonal(A), 2.0)
donde Diagonal
devuelve un objeto DiagonalView
que es una especie de adaptador ligero.
Ahora aquí está la (s) pregunta (s). Utilizaré una operación de matriz muy simple como ejemplo. Quiero definir una función como
template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);
que hace lo más obvio que su nombre sugiere. Quiero poder pasar una matriz que no sea fiel a la bondad o una instancia de una subclase de MatrixView
. El prototipo de lo escrito anteriormente no funciona para declaraciones como
Scale(Diagonal(A), 2.0);
porque el objeto devuelto por DiagonalView
Diagonal
es un temporal, y Scale
toma una referencia no constante, que no puede aceptar un temporal. ¿Hay alguna manera de hacer funcionar esto? Traté de usar SFINAE, pero no lo entiendo muy bien, y no estoy seguro de si eso resolvería el problema. Para mí es importante que se puedan llamar a estas funciones con plantilla sin proporcionar una lista explícita de argumentos de plantilla (quiero una instanciación implícita). Idealmente, la declaración anterior podría funcionar como está escrita.
Editar: (pregunta de seguimiento)
Como OSE respondió a continuación acerca de las referencias rvalue y temporales, ¿Hay una manera de definir dos versiones de la escala, uno que toma una referencia rvalue no constante para no vistas, y una que toma una vista de valor por paso? El problema es diferenciar estos dos en el momento de la compilación de forma tal que funcione la instanciación implícita.
actualización
he cambiado la jerarquía de clases a
ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView
La razón WritableMatrixView
es distinta de WritableMatrix
es que la vista se debe pasar por alrededor de referencia constante, mientras que la las matrices mismas deben pasarse por non-const ref, por lo que las funciones del miembro de acceso tienen diferente const-ness. Ahora funciones como escala pueden ser definidos como
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);
}
Nota que hay dos versiones, una para una vista const, y una versión no const para matrices reales. Esto significa que para funciones como Mult(A, B, C)
, necesitaré 8 sobrecargas, pero al menos funciona. Lo que no funciona, sin embargo, es usar estas funciones dentro de otras funciones.Verá, cada clase similar a View
contiene un miembro View
de lo que está viendo; por ejemplo, en la expresión Diagonal(SubMatrix(A))
, la función Diagonal
devuelve un objeto de tipo DiagonalView<SubMatrixView<T> >
, que necesita conocer el tipo totalmente derivado de A
. Ahora, supongamos que dentro de Scale
llamo a alguna otra función como esta, que toma una vista base o una referencia matricial. Eso no funcionaría porque la construcción de los necesarios View
requiere el tipo derivado del argumento de Scale; información que no tiene. Todavía estoy trabajando para encontrar una solución a esto.
actualización
he utilizado lo que es efectivamente una versión de cosecha de enable_if de Boost para seleccionar entre dos versiones diferentes de una función como Scale
. Todo se reduce a etiquetar todas mis clases de matrix y view con etiquetas typedef adicionales que indican si son legibles y escribibles, y view o non-view. Al final, todavía necesito 2^N sobrecargas, pero ahora N es solo el número de argumentos no const. Para obtener el resultado final, consulte here (es poco probable que se vuelva a modernizar de nuevo).
¿Por qué razón Scale() no acepta el parámetro por referencia de referencia? – Naveen
La escala debe realmente escalar su argumento, por lo tanto no puede ser const. –
¿Por qué la escala modifica la matriz temporal? –