То, что уже объяснил Конрад, можно далее использовать для поддержки вложенных вызовов операторов, все выполняемые лениво. В примере Конрада у него есть объект выражения, который может хранить ровно два аргумента, ровно для двух операндов одной операции. Проблема в том, что он будет выполнять только одно подвыражение лениво, что приятно объясняет концепцию ленивого вычисления, выраженную простыми терминами, но существенно не повышает производительность. Другой пример также хорошо показывает, как можно применить operator()
, чтобы добавить только некоторые элементы, используя этот объект выражения. Но чтобы оценить произвольные сложные выражения, нам нужен механизм, который может хранить структуру этого тоже. Мы не можем обойти шаблоны для этого. И имя для этого - expression templates
. Идея состоит в том, что один шаблонный объект выражения может рекурсивно хранить структуру некоторого произвольного подвыражения, например, дерево, где операции - это узлы, а операнды - это дочерние узлы. Для очень хорошего объяснения, которое я только что нашел сегодня (через несколько дней после того, как написал код ниже), см. здесь .
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; }
};
Это будет хранить любую операцию сложения, даже вложенную, как видно из следующего определения оператора + для простого типа точки:
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);
}
Теперь, если у вас есть
Point p1 = { 1, 2 }, p2 = { 3, 4 }, p3 = { 5, 6 };
p1 + (p2 + p3); // returns AddOp< Point, AddOp<Point, Point> >
Теперь вам просто нужно перегрузить operator =, добавить подходящий конструктор для типа Point и принять AddOp. Измените его определение на:
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; }
};
И добавьте соответствующие get_x и get_y в AddOp в качестве функций-членов:
int get_x() const {
return lhs.get_x() + rhs.get_x();
}
int get_y() const {
return lhs.get_y() + rhs.get_y();
}
Обратите внимание, что мы не создали временные объекты типа Point. Это могла быть большая матрица со многими полями. Но в то время, когда нужен результат, мы вычисляем его лениво .