C++ for Scientists - Technische Universität Dresden

C++ for Scientists - Technische Universität Dresden C++ for Scientists - Technische Universität Dresden

math.tu.dresden.de
from math.tu.dresden.de More from this publisher
03.12.2012 Views

154 CHAPTER 5. META-PROGRAMMING Discussion 5.1 The drawback is that if the entries are accessed multiple times the sum is recomputed. On the other hand, most expressions are only used once and this is not a problem. An example where vector entries are accessed several times is A ∗ (x+y). Here, it is preferable to compute a true vector first instead of computing the matrix vector product on the expression template. 12 To evaluate w= x + y we also need an assignment operator for vector sum: template class vector sum; // forward declaration template class vector { // ... vector& operator=(const vector sum& that) { check size(size(that)); for (int i= 0; i < my size; ++i) data[i]= that[i]; return ∗this; } }; The assignment runs a loop over w and that. As that is an object of type vector sum the expression that[i] computes x[i] + y[i]. In contrast to the implementationn in Section 5.3.1 we have now • Only one loop; • No temporary vector; • No additional memory allocation and deallocation; and • No addional data reads and writes. In fact, the same operations are performed as in the loop for (int i= 0; i < size(w); ++i) w[i] = x[i] + y[i]; The cost to create a vector sum object is negligible. The object will be kept on the stack and does not require memory allocation. Even that little effort for creating the object will be optimized away by most compilers with static code analysis. What happens when we like to add three vectors? The naïve implementation from § 5.3.1 returns a vector and this vector can be added to another vector. Our approach returns a vector sum and we have no addition for vector sum and vector. Thus we would need another ET class and an according operation: template class vector sum3 { void check index(int i) const { assert(i >= 0 && i < size(v1)); } public: vector sum3(const vector& v1, const vector& v2, const vector& v3) : v1(v1), v2(v2), v3(v3) { assert(size(v1) == size(v2)); assert(size(v1) == size(v3)); 12 TODO: Shall we provide a solution for this as well? This something that is over-due in MTL4 anyway.

5.3. EXPRESSION TEMPLATES 155 } friend int size(const vector sum3& x) { return size(x.v1); } T operator[](int i) const { check index(i); return v1[i] + v2[i] + v3[i]; } private: const vector& v1, v2, v3; }; template vector sum3 inline operator+(const vector sum& x, const vector& y) { return vector sum3(x.v1, x.v2, y); } Furthermore, vector sum must declare our new plus operator as friend to access its private members and vector needs an assignment for vector sum3. This becomes increasingly annoying. Also, what happens if we perform the second addition first w= x + (y + z)? Then we need another plus operator. What if some of the vectors are multiplied by a scalar, e.g., w= x + dot(x, y) ∗ y + 4.3 ∗ z, and this scalar product is also implemented by an ET? Our implementation effort runs into combinatorial explosion and we need a more flexible solution that we introduce in the next section. 5.3.3 Generic Expression Templates So far we started from a specific class (vector) and generalized the implementation gradually. Although this can help us to understand the mechanism, we like to go now to the general version that takes arbitrary vector types: template vector sum inline operator+(const V1& x, const V2& y) { return vector sum(x, y); } We now need an expression class with arbitrary arguments: template class vector sum { typedef vector sum self; void check index(int i) const { assert(i >= 0 && i < size(v1)); } public: vector sum(const V1& v1, const V2& v2) : v1(v1), v2(v2) { assert(size(v1) == size(v2)); } ???? operator[](int i) const { check index(i); return v1[i] + v2[i]; } friend int size(const self& x) { return size(x.v1); } private: const V1& v1;

5.3. EXPRESSION TEMPLATES 155<br />

}<br />

friend int size(const vector sum3& x) { return size(x.v1); }<br />

T operator[](int i) const { check index(i); return v1[i] + v2[i] + v3[i]; }<br />

private:<br />

const vector& v1, v2, v3;<br />

};<br />

template <br />

vector sum3 inline operator+(const vector sum& x, const vector& y)<br />

{<br />

return vector sum3(x.v1, x.v2, y);<br />

}<br />

Furthermore, vector sum must declare our new plus operator as friend to access its private<br />

members and vector needs an assignment <strong>for</strong> vector sum3. This becomes increasingly annoying.<br />

Also, what happens if we per<strong>for</strong>m the second addition first w= x + (y + z)? Then we<br />

need another plus operator. What if some of the vectors are multiplied by a scalar, e.g.,<br />

w= x + dot(x, y) ∗ y + 4.3 ∗ z, and this scalar product is also implemented by an ET? Our implementation<br />

ef<strong>for</strong>t runs into combinatorial explosion and we need a more flexible solution that<br />

we introduce in the next section.<br />

5.3.3 Generic Expression Templates<br />

So far we started from a specific class (vector) and generalized the implementation gradually.<br />

Although this can help us to understand the mechanism, we like to go now to the general version<br />

that takes arbitrary vector types:<br />

template <br />

vector sum inline operator+(const V1& x, const V2& y)<br />

{<br />

return vector sum(x, y);<br />

}<br />

We now need an expression class with arbitrary arguments:<br />

template <br />

class vector sum<br />

{<br />

typedef vector sum self;<br />

void check index(int i) const { assert(i >= 0 && i < size(v1)); }<br />

public:<br />

vector sum(const V1& v1, const V2& v2) : v1(v1), v2(v2)<br />

{<br />

assert(size(v1) == size(v2));<br />

}<br />

???? operator[](int i) const { check index(i); return v1[i] + v2[i]; }<br />

friend int size(const self& x) { return size(x.v1); }<br />

private:<br />

const V1& v1;

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!