Ich habe derzeit eine Klassenhierarchie wie
MatrixBase -> DenseMatrix
-> (other types of matrices)
-> MatrixView -> TransposeView
-> DiagonalView
-> (other specialized views of matrices)
MatrixBase
ist eine abstrakte Klasse, die Implementierer dazu zwingt, operator()(int,int) und ähnliche Dinge zu definieren; sie repräsentiert 2-dimensionale Arrays von Zahlen. MatrixView
steht für eine (möglicherweise veränderbare) Art und Weise, eine Matrix zu betrachten, z. B. durch Transponieren oder durch Aufnahme einer Untermatrix. Der Punkt von MatrixView
ist es, etwas sagen zu können wie
Scale(Diagonal(A), 2.0)
wobei Diagonal
gibt eine DiagonalView
Objekt, das eine Art leichtgewichtiger Adapter ist.
Nun die Frage(n). Ich werde eine sehr einfache Matrixoperation als Beispiel verwenden. Ich möchte eine Funktion definieren wie
template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);
die genau das tut, was der Name nahelegt. Ich möchte in der Lage sein, entweder eine echte Nicht-Ansichtsmatrix oder eine Instanz einer Unterklasse von MatrixView
. Der oben beschriebene Prototyp funktioniert nicht für Anweisungen wie
Scale(Diagonal(A), 2.0);
weil die DiagonalView
Objekt zurückgegeben von Diagonal
ist ein temporäres, und Scale
nimmt eine nicht-konstante Referenz, die keine temporäre akzeptieren kann. Gibt es eine Möglichkeit, dies zu erreichen? Ich habe versucht, SFINAE zu verwenden, aber ich verstehe es nicht so gut, und ich bin nicht sicher, ob das Problem damit gelöst werden kann. Für mich ist es wichtig, dass diese Template-Funktionen aufgerufen werden können, ohne eine explizite Template-Argumentliste anzugeben (ich möchte eine implizite Instanziierung). Im Idealfall könnte die obige Anweisung wie geschrieben funktionieren.
Edit: (Folgefrage)
Wie sbi unten über rvalue-Referenzen und Temporaries geantwortet hat, gibt es eine Möglichkeit, zwei Versionen von Scale zu definieren, eine, die eine nicht-konstante rvalue-Referenz für Nicht-Ansichten nimmt, und eine, die eine pass-by-value-Ansicht nimmt? Das Problem ist, zwischen diesen beiden zur Kompilierzeit so zu unterscheiden, dass die implizite Instanziierung funktioniert.
更新情報
Ich habe die Klassenhierarchie geändert in
ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView
Der Grund WritableMatrixView
ist verschieden von WritableMatrix
ist, dass die Ansicht per Const-Referenz weitergegeben werden muss, während die Matrizen selbst per Non-Const-Referenz weitergegeben werden müssen, so dass die Accessor-Member-Funktionen unterschiedliche Const-Eigenschaften haben. Jetzt können Funktionen wie Scale definiert werden als
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);
}
Beachten Sie, dass es zwei Versionen gibt, eine für eine Const-Ansicht und eine Non-Const-Version für aktuelle Matrizen. Das bedeutet für Funktionen wie Mult(A, B, C)
Ich brauche 8 Überlastungen, aber zumindest funktioniert es. Was allerdings nicht funktioniert, ist die Verwendung dieser Funktionen innerhalb anderer Funktionen. Sie sehen, jede View
-ähnliche Klasse enthält ein Mitglied View
von dem, was es betrachtet; zum Beispiel in dem Ausdruck Diagonal(SubMatrix(A))
は、その Diagonal
Funktion gibt ein Objekt des Typs DiagonalView<SubMatrixView<T> >
, die den vollständig abgeleiteten Typ von A
. Nehmen wir nun an, dass innerhalb Scale
Ich rufe eine andere Funktion auf, die entweder eine Basisansicht oder eine Matrixreferenz benötigt. Das würde fehlschlagen, weil die Konstruktion der benötigten View
erfordern den abgeleiteten Typ des Arguments von Scale; Informationen, die es nicht hat. Ich arbeite noch daran, eine Lösung für dieses Problem zu finden.
更新情報
Ich habe verwendet, was ist effektiv eine hausgemachte Version von Boost's enable_if, um zwischen zwei verschiedenen Versionen einer Funktion wie wählen Scale
. Es läuft darauf hinaus, alle meine Matrix- und View-Klassen mit zusätzlichen typedef-Tags zu versehen, die angeben, ob sie lesbar oder schreibbar sind und ob es sich um View oder Non-View handelt. Am Ende brauche ich immer noch 2^N Überladungen, aber N ist jetzt nur noch die Anzahl der Nicht-Konst-Argumente. Das Endergebnis finden Sie in der aquí (es ist unwahrscheinlich, dass es noch einmal ernsthaft überarbeitet wird).