CRTP: determina il tipo di classe derivata nella funzione della classe di base per consentire il riutilizzo del codice
Diciamo che ho una classe modello CRTP per matrici
template<class T, class Derived>
class MatrixBase{
private:
//...
public:
Derived some_function(const Derived &other){
Derived& self = (Derived&)*this; // In my application I cant use static_cast.
// Some calculations..., which will determine the below
// defined variables "some_number_of_rows" and "some_number_of_cols"
// If Derived = DynamicMatrix<T>, then result should be declared as:
DynamicMatrix<T> result(some_number_of_rows, some_number_of_cols);
// while if Derived = StaticMatrix<T, Rows, Cols>, then result should be declared as:
StaticMatrix<T, some_number_of_rows, some_number_of_cols> result;
// Perform some more calculations...
return result;
}
};
template<class T>
class DynamicMatrix{
private:
size_t n_rows, n_cols;
T *data;
public:
DynamicMatrix(const size_t n_rows, const size_t n_cols);
// ...
};
template<class T, int Rows, int Cols>
class StaticMatrix{
private:
size_t n_rows = Rows, n_cols = Cols;
T data[Rows * Cols];
public:
StaticMatrix() {}
// ...
};
Come posso verificare il tipo di classe derivata MatrixBase::some_function(const Derived &other)
per utilizzare questa funzione di base in entrambe le classi derivate? , impedendo la necessità di ridefinire/sovrascrivere/duplicare il codice in queste classi separatamente. In questo caso è fondamentalmente solo la dichiarazione della result
matrice che mi richiede di verificare il tipo di classe Derived, poiché la dichiarazione è diversa a seconda che si tratti di una matrice a dimensione fissa o dinamica. Sono benvenute anche altre soluzioni oltre al controllo del tipo.
NOTA : non posso utilizzare le funzionalità standard a causa della natura della mia applicazione.
EDIT : la funzione some_number_of_rows
e some_number_of_cols
nella funzione di esempio non sono generalmente constexpr, poiché dipendono dalla funzione e dalla dimensione della matrice dell'oggetto. Ad esempio, per una transpose
funzione, il risultato dovrebbe avere dimensione <Derived.n_cols, Derived.n_rows
e, nel caso di un prodotto scalare per colonna, <1, Derived.n_cols>
.
Risposte
Questo è un problema impegnativo. Fondamentalmente, some_number_of_rows
e some_number_of_cols
deve essere constexpr
in caso di StaticMatrix
e non può essere constexpr
in caso di DynamicMatrix
.
Una soluzione consiste nel delegare la creazione di nuove matrici alla classe derivata. Eseguirà il calcolo delle dimensioni come constexpr
o meno constexpr
, a seconda di quale funzioni per esso.
Un altro consiste nell'effettuare il calcolo della dimensione nella classe CRTP due volte, una volta come constexpr
e una volta come not constexpr
, e passare entrambi i risultati alla funzione di creazione dell'oggetto derivato: constexpr
viene passato come argomento modello e non- constexpr
come argomento normale. La funzione di creazione è specializzata per matrici statiche e dinamiche. La versione statica ignora i non constexpr
parametri e viceversa.
A quanto ho capito, potresti aggiungere quel metodo di fabbrica all'interno Derived
:
Se some_number_of_rows
, some_number_of_cols
può essere constexpr (per soddisfare il più vincolato Derived
) in entrambi i casi, potresti fare qualcosa del tipo:
template<class T>
class DynamicMatrix
{
// ...
template <std::size_t Row, std::size_t Col>
static DynamicMatrix<T> Create() { return DynamicMatrix(Row, Col); }
};
template <class T, int Rows, int Cols>
class StaticMatrix{
// ...
template <std::size_t Row, std::size_t Col>
static StaticMatrix<T, Row, Col> Create() { return {}; }
};
Insieme a
auto result = Derived::Create<some_number_of_rows, some_number_of_cols>();
altrimenti devi spostare quel calcolo anche nelle funzioni derivate:
template<class T>
class DynamicMatrix
{
// ...
DynamicMatrix<T> CreateEmptyTransposed() const { return DynamicMatrix(n_cols, n_rows); }
};
template <class T, int Rows, int Cols>
class StaticMatrix{
// ...
StaticMatrix<T, Cols, Row> CreateEmptyTransposed() const { return {}; }
};
Insieme a
auto result = self.CreateEmptyTransposed();