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

194 CHAPTER 6. INHERITANCE Our compilers are so sophisticated, they certainly handle de Morgan’s law perfectly. Negating the equality operator is something we can do on every type that has an equality operator. We could copy-and-past this code snippet and just replace the type of the argument. Alternatively, we can write a class like this: template struct unequality { bool operator!=(const T& that) const { return !(static cast(∗this) == that); } }; and derive from it: class point : public unequality { ... }; This mutual dependency: • One class is derived from the other and • The latter takes the derived class’ type as template argument is somewhat confusing at the first view. Essential for this to work is that the code of a template class member is only generated when the class is instantiated and the function is actually called. At the time the template class ‘unequality is parsed, the compiler checks only the correctness of the syntax. When we write int main (int argc, char∗ argv[]) { point p1(3, 4), p2(3, 5); std::cout ≪ ”p1 != p2 is ” ≪ (p1 != p2 ? ”true” : ”false”) ≪ ’\n’; } return 0 ; fter the definition of unequality and point both types are completely known to the compiler. What happens when we call p1 != p2? 1. The compiler searches for operator!= in class point → without success. 2. The compiler looks for operator!= in the base class unequality → with success. 3. The this pointer of unequality refers a component of point’s this pointer. 4. Both types are completely known and we can statically down-cast the this pointer to point. 5. Since we know that the this pointer of unequality is an up-casted this pointer to point 3 we are save to down-cast it to its original type. 6. The equality operator for point is called. Its implementation is already known at this point because the code of unequality’s operator!= is not generated before the instantiation of point. 3 Unless the first argument is really of type unequality. There are also ways to impede this, e.g. http: //en.wikipedia.org/wiki/Barton-Nackman_trick but we used this unary operator notation for the sake of simplicity.

6.5. BARTON-NACKMAN TRICK 195 Likewise every class U with an equality operator can be derived from unequality. A collection of such CRTP templates for operator defaults is provided by Boost.Operators from Jeremy Siek and David Abrahams. Alternatively to the above implementation where the this pointer is dereferred and casted as reference, one can cast the pointer first and derefer it afterwards: template struct unequality { bool operator!=(const T& that) const { return !(∗static cast(this) == that); } }; There is no difference, this is just a question of taste. 6.5.2 A Reusable Access Operator ⇒ matrix crtp example.cpp We still owe the reader the reusable implementation of the matrix bracket operator promised in Section 3.7.4. Back then we did not know enough language features. First of all we had no templates which are indispensable for a proxy. We will show you why. Say we have a matrix class as in § 3.7.4 and we just want to call the binary operator() from the unary operator[] via a proxy: class matrix; // Forward declaration class simple bracket proxy { public: simple bracket proxy(matrix& A, int r) : A(A), r(r) {} double& operator[](int c){ return A(r, c); } private: matrix& A; int r; }; class matrix { // ... double& operator()(int r, int c) { ... } }; simple bracket proxy operator[](int r) { return simple bracket proxy(∗this, r); } This does not compile because operator[] from simple bracket proxy calls operator() from matrix but this is not defined yet. The forward declaration of matrix is not sufficient because we need the complete definition of matrix not only the assertion that the type exist. Vice versa if we define matrix first, we would miss the constructor of simple bracket proxy in the operator[] implementation.

6.5. BARTON-NACKMAN TRICK 195<br />

Likewise every class U with an equality operator can be derived from unequality. A collection<br />

of such CRTP templates <strong>for</strong> operator defaults is provided by Boost.Operators from Jeremy<br />

Siek and David Abrahams.<br />

Alternatively to the above implementation where the this pointer is dereferred and casted as<br />

reference, one can cast the pointer first and derefer it afterwards:<br />

template <br />

struct unequality<br />

{<br />

bool operator!=(const T& that) const { return !(∗static cast(this) == that); }<br />

};<br />

There is no difference, this is just a question of taste.<br />

6.5.2 A Reusable Access Operator<br />

⇒ matrix crtp example.cpp<br />

We still owe the reader the reusable implementation of the matrix bracket operator promised<br />

in Section 3.7.4. Back then we did not know enough language features.<br />

First of all we had no templates which are indispensable <strong>for</strong> a proxy. We will show you why.<br />

Say we have a matrix class as in § 3.7.4 and we just want to call the binary operator() from the<br />

unary operator[] via a proxy:<br />

class matrix; // Forward declaration<br />

class simple bracket proxy<br />

{<br />

public:<br />

simple bracket proxy(matrix& A, int r) : A(A), r(r) {}<br />

double& operator[](int c){ return A(r, c); }<br />

private:<br />

matrix& A;<br />

int r;<br />

};<br />

class matrix<br />

{<br />

// ...<br />

double& operator()(int r, int c) { ... }<br />

};<br />

simple bracket proxy operator[](int r)<br />

{<br />

return simple bracket proxy(∗this, r);<br />

}<br />

This does not compile because operator[] from simple bracket proxy calls operator() from matrix but<br />

this is not defined yet. The <strong>for</strong>ward declaration of matrix is not sufficient because we need the<br />

complete definition of matrix not only the assertion that the type exist. Vice versa if we define<br />

matrix first, we would miss the constructor of simple bracket proxy in the operator[] implementation.

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

Saved successfully!

Ooh no, something went wrong!