To motivate the need for expression templates, consider typical numerical linear algebra operations, such as adding together two matrices or vectors,[75] such as in the following:
D = A + B + C;
In naive implementations, this expression would result in a number of temporaries—one for A+B, and one for (A+B)+C. When these variables represent immense matrices or vectors, the coincident drain on resources is unacceptable. Expression templates allow you to use the same expression without temporaries.
In the following sample program, we define a MyVector class to simulate mathematical vectors of any size. We use a non-type template argument for the length of the vector. We also define a MyVectorSum class to act as a proxy class for a sum of MyVector objects. This allows us to use lazy evaluation, so the addition of vector components is performed on demand without the need for temporaries.
//: C05:MyVector.cpp
// Optimizes away temporaries via templates
#include
#include
#include
using namespace std;
// A proxy class for sums of vectors
template
template
class MyVector {
T data[N];
public:
MyVector
for (size_t i = 0; i < N; ++i)
data[i] = right.data[i];
return *this;
}
MyVector
const T& operator[](size_t i) const {
return data[i];
}
T& operator[](size_t i) {
return data[i];
}
};
// Proxy class hold references; uses lazy addition
template
class MyVectorSum {
const MyVector
const MyVector
public:
MyVectorSum(const MyVector
const MyVector
: left(lhs), right(rhs) {}
T operator[](size_t i) const {
return left[i] + right[i];
}
};
// Operator to support v3 = v1 + v2
template
MyVector
MyVector
for (size_t i = 0; i < N; ++i)
data[i] = right[i];
return *this;
}
// operator+ just stores references
template
inline MyVectorSum
operator+(const MyVector
const MyVector
return MyVectorSum
}
// Convenience functions for the test program below
template
void init(MyVector
for (size_t i = 0; i < N; ++i)
v[i] = rand() % 100;
}
template
void print(MyVector
for (size_t i = 0; i < N; ++i)
cout << v[i] << ' ';
cout << endl;
}
int main() {
MyVector
init(v1);
print(v1);
MyVector
init(v2);
print(v2);
MyVector
v3 = v1 + v2;
print(v3);
MyVector
// Not yet supported:
//! v4 = v1 + v2 + v3;
} ///:~.
The MyVectorSum class does no computation when it is created; it merely holds references to the two vectors to be added. It is only when you access a component of a vector sum that it is calculated (see its operator[]( )). The overload of the assignment operator for MyVector that takes a MyVectorSum argument is for an expression such as:.
v1 = v2 + v3; // add two vectors
When the expression v1+v2 is evaluated, a MyVectorSum object is returned (or actually, inserted inline, since that operator+( ) is declared inline). This is a small, fixed-size object (it holds only two references). Then the assignment operator mentioned above is invoked:
v3.operator=
This assigns to each element of v3 the sum of the corresponding elements of v1 and v2, computed in real time. No temporary MyVector objects are created.
This program does not support an expression that has more than two operands, however, such as
v4 = v1 + v2 + v3;
The reason is that after the first addition, a second addition is attempted:
(v1 + v2) + v3;
which would require an operator+( ) with a first argument of MyVectorSum and a second argument of type MyVector. We could attempt to provide a number of overloads to meet all situations, but it is better to let templates do the work, as in the following version of the program.
//: C05:MyVector2.cpp
// Handles sums of any length with expression templates
#include
#include
#include
using namespace std;
// A proxy class for sums of vectors
template
template
class MyVector {
T data[N];
public:
MyVector
for (size_t i = 0; i < N; ++i)
data[i] = right.data[i];
return *this;
}
template
MyVector
operator=(const MyVectorSum
const T& operator[](size_t i) const {
return data[i];
}
T& operator[](size_t i) {
return data[i];
}
};
// Allows mixing MyVector and MyVectorSum
template
class MyVectorSum {
const Left& left;
const Right& right;
public:
MyVectorSum(const Left& lhs, const Right& rhs)
: left(lhs), right(rhs) {}
T operator[](size_t i) const {
return left[i] + right[i];
}
};
template
template
MyVector
MyVector
operator=(const MyVectorSum
for (size_t i = 0; i < N; ++i)
data[i] = right[i];
return *this;
}
// operator+ just stores references
template