J'ai récemment trébuché sur des modèles d'expression en C++. Il y a une chose que je ne comprends pas vraiment à propos de leur implémentation, et c'est pourquoi la classe de base est nécessaire (à partir de laquelle tous les autres objets relatifs à l'expression de modèle dérivent d'une manière CRTP). Un exemple simple qui fait l'addition et la multiplication scalaire sur les vecteurs (objets de type Vec
, sans classe de base):Modèles d'expression C++ - Pourquoi la classe de base?
#include <vector>
#include <iostream>
using namespace std;
class Vec
{
vector<double> data;
public:
template<typename E>
Vec(E expr)
{
data = vector<double>(expr.size());
for (int i = 0; i < expr.size(); i++)
data[i] = expr[i];
}
Vec(int size)
{
data = vector<double>(size);
for (int i = 0; i < size; i++)
data[i] = i;
}
double operator [] (int idx) {
return data[idx];
}
int size() { return data.size(); }
bool operator < (Vec &rhs)
{
return (*this)[0] < rhs[0];
}
bool operator > (Vec &rhs)
{
return (*this)[0] > rhs[0];
}
};
template<typename E1, typename E2>
class VecAdd
{
E1 vec_expr1;
E2 vec_expr2;
public:
VecAdd(E1 vec_expr1, E2 vec_expr2) : vec_expr1(vec_expr1), vec_expr2(vec_expr2)
{}
double operator [] (int idx) { return vec_expr1[idx] + vec_expr2[idx]; }
int size() { return vec_expr1.size(); }
};
template<typename E>
class ScalarMult
{
E vec_expr;
double scalar;
public:
ScalarMult(double scalar, E vec_expr) : scalar(scalar), vec_expr(vec_expr)
{}
double operator [] (int idx) { return scalar*vec_expr[idx]; }
int size() { return vec_expr.size(); }
};
template<typename E1, typename E2>
VecAdd<E1, E2> operator + (E1 vec_expr1, E2 vec_expr2)
{
return VecAdd<E1, E2>(vec_expr1, vec_expr2);
}
template<typename E>
ScalarMult<E> operator * (double scalar, E vec_expr)
{
return ScalarMult<E>(scalar, vec_expr);
}
int main()
{
Vec term1(5);
Vec term2(5);
Vec result = 6*(term1 + term2);
Vec result2 = 4 * (term1 + term2 + term1);
//vector<Vec> vec_vector = {result, result2}; does not compile
vector<Vec> vec_vector;
vec_vector = { result2, result }; //compiles
vec_vector.clear();
vec_vector.push_back(result);
vec_vector.push_back(result2); //all this compiles
for (int i = 0; i < result.size(); i++)
cout << result[i] << " ";
cout << endl;
system("pause");
return 0;
}
Le code ci-dessus compiles (sauf pour la ligne indiquée), et il évalue les expressions simples dans la principale fonction sans faute. Si les expressions sont affectées à un objet de type Vec
et affectent leur contenu à un objet Vec
, se détruisant dans le processus dans tous les cas, pourquoi est-il nécessaire pour une classe de base? (Comme indiqué dans l'article this Wikipedia)
EDIT:
Je sais que ce code est un peu en désordre et mauvaise (copie ou inutiles, etc.), mais je ne prévois pas d'utiliser ce code spécifique. Ceci est juste pour illustrer que les modèles d'expression fonctionnent dans cet exemple sans la classe de base CRTP - et j'essaie de comprendre exactement pourquoi cette classe de base est nécessaire.
Qu'est-ce que 'E expr'? S'il s'agit d'une structure de données, pourquoi la copiez-vous en tant que paramètre, puis de nouveau dans la fonction? Pourquoi ne pas utiliser le redimensionnement ou la fonction d'assignation (en fonction des types valides de E) dans la fonction? Pourquoi ne pas utiliser 'vector :: size_type' pour stocker le type du vecteur? Je ne comprends pas la logique de vos opérateurs < >. Vector prend déjà en charge < > opérateurs pour les comparaisons lexicographiques. –
S'il vous plaît voir mon edit –
Votre 'opérateur +' correspond, eh bien, à propos de tout sous le soleil. –