Ce que Konrad déjà expliqué peut être renforcée, afin de soutenir les appels imbriqués des opérateurs, le tout exécuté paresseusement. Dans Konrad exemple, il a une expression de l'objet qui peut stocker exactement deux arguments, pour exactement deux opérandes d'une opération. Le problème est qu'elle ne fait qu'exécuter une sous-expression paresseusement, qui explique bien le concept d'évaluation différée mettre en termes simples, mais n'améliore pas les performances sensiblement. L'autre exemple montre aussi comment on peut appliquer le operator()
ajouter quelques éléments à l'aide de l'expression de l'objet. Mais à l'évaluation arbitraire des expressions complexes, nous avons besoin d'un mécanisme qui peut stocker de la structure de ce trop. Nous ne pouvons pas obtenir autour de modèles de le faire. Et le nom de c'est - expression templates
. L'idée est que l'un basé sur un modèle objet d'expression peut stocker la structure de certains arbitraire sous-expression de manière récursive, comme un arbre, où les opérations sont les nœuds, et les opérandes sont les enfants des nœuds. Pour une très bonne explication je viens de trouver aujourd'hui (quelques jours après, j'ai écrit le code ci-dessous) voir ici.
template<typename Lhs, typename Rhs>
struct AddOp {
Lhs const& lhs;
Rhs const& rhs;
AddOp(Lhs const& lhs, Rhs const& rhs):lhs(lhs), rhs(rhs) {
// empty body
}
Lhs const& get_lhs() const { return lhs; }
Rhs const& get_rhs() const { return rhs; }
};
Qui va stocker toute opération d'addition, même imbriquées les unes, comme on le voit par la définition suivante d'un opérateur+ pour un simple point de type:
struct Point { int x, y; };
// add expression template with point at the right
template<typename Lhs, typename Rhs> AddOp<AddOp<Lhs, Rhs>, Point>
operator+(AddOp<Lhs, Rhs> const& lhs, Point const& p) {
return AddOp<AddOp<Lhs, Rhs>, Point>(lhs, p);
}
// add expression template with point at the left
template<typename Lhs, typename Rhs> AddOp< Point, AddOp<Lhs, Rhs> >
operator+(Point const& p, AddOp<Lhs, Rhs> const& rhs) {
return AddOp< Point, AddOp<Lhs, Rhs> >(p, rhs);
}
// add two points, yield a expression template
AddOp< Point, Point >
operator+(Point const& lhs, Point const& rhs) {
return AddOp<Point, Point>(lhs, rhs);
}
Maintenant, si vous avez
Point p1 = { 1, 2 }, p2 = { 3, 4 }, p3 = { 5, 6 };
p1 + (p2 + p3); // returns AddOp< Point, AddOp<Point, Point> >
Il vous suffit maintenant de surcharge de l'opérateur= et ajouter un constructeur approprié pour le type de Point et accepter AddOp. Changer sa définition:
struct Point {
int x, y;
Point(int x = 0, int y = 0):x(x), y(y) { }
template<typename Lhs, typename Rhs>
Point(AddOp<Lhs, Rhs> const& op) {
x = op.get_x();
y = op.get_y();
}
template<typename Lhs, typename Rhs>
Point& operator=(AddOp<Lhs, Rhs> const& op) {
x = op.get_x();
y = op.get_y();
return *this;
}
int get_x() const { return x; }
int get_y() const { return y; }
};
Et ajouter les get_x et get_y en AddOp que les fonctions de membres:
int get_x() const {
return lhs.get_x() + rhs.get_x();
}
int get_y() const {
return lhs.get_y() + rhs.get_y();
}
Notez comment nous n'avons pas créé de toute temporaires de type Point. Il pourrait avoir été un grand matrice avec de nombreux champs. Mais à l'époque, le résultat est nécessaire, nous le calculons paresseusement.