Konrad が既に説明したことは、すべて遅延実行されるオペレーターのネストされた呼び出しをサポートするためにさらに追加できます。Konrad の例では、1 つの演算の正確に 2 つのオペランドに対して、正確に 2 つの引数を格納できる式オブジェクトがあります。問題は、 1 つの部分式のみを遅延して実行することです。これは、遅延評価の概念を簡単に説明するとうまく説明できますが、パフォーマンスは大幅に向上しません。operator()
もう 1 つの例は、その式オブジェクトを使用して一部の要素のみを追加する方法をよく示しています。しかし、任意の複雑な式を評価するには、その構造も格納できるメカニズムが必要です。それを行うためにテンプレートを回避することはできません。そしてその名前はexpression templates
. アイデアは、1 つのテンプレート化された式オブジェクトが、ツリーのように任意のサブ式の構造を再帰的に格納できるというものです。ここで、操作はノードであり、オペランドは子ノードです。今日(以下のコードを書いてから数日後)見つけた非常に良い説明については、こちらを参照してください。
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; }
};
単純なポイント型の operator+ の次の定義からわかるように、ネストされたものであっても、すべての加算演算が格納されます。
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 型のテンポラリを作成していないことに注意してください。多くのフィールドを持つ大きなマトリックスである可能性があります。しかし、結果が必要なときは、遅延計算します。