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

92 CHAPTER 4. GENERIC PROGRAMMING can trace back the error. Please try to compile this example on different compilers at your availability and see if you can make any sense out of the error messages. If you run into such lengthy error message 1 DON’T PANIC! First, look at the error itself and take out what is useful for you, e.g. missing “operator>” or something not assignable, i.e. missing “operator=” or something const that should not. Then find in the call stack your innermost code that is the part of your program where you call somebody else’s template function. Stare for a while at this and its preceding lines because this is the most likely place where the error is made. Does a type of the template function function’s argument is missing an operator or function as mentioned in the error? Do not get scared away from this, often the problem is much simpler than it seems from the never-ending error message. From our experience, most errors in template functions one can find faster than run-time errors. Another question we have not answered so far is what happens if we use two different types: unsigned u1= 2; int i= 3; std::cout ≪ ”The maximum of u1 and i is ” ≪ max(u1, i) ≪ ’\n’; The compiler tell us — this time briefly — something like Error: no match for function call ≫max(unsigned int&, int)≪ Indeed, we assumed that both types are the same. Now can we write a template function with two template parameters? Of course, we can. But that does not help us much here because we would not know what return type the function had. There are different options. First we could add a non-templated function like: int inline max (int a, int b) { return a > b ? a : b; } This can be called with mixed types and the unsigned argument would be implicitly converted into an int. But what would happen if we also add a function for unsigned? int max(unsigned a, unsigned b) { return a > b ? a : b; } Shall the int be converted into an unsigned or vice versa? The compiler does not know and will complain about this ambibuity. At any rate, adding non-templated overloads to the templated implemention is far from being elegant nor productive. So, we remove all non-templated overloads and look what we can do in the function call. We can explicitly convert one argument to the type of the other: unsigned u1= 2; int i= 3; std::cout ≪ ”The maximum of u1 and i is ” ≪ max(int(u1), i) ≪ ’\n’; Now max is called with two ints. Another option is specifying the template type explicitly in the function call: unsigned u1= 2; int i= 3; std::cout ≪ ”The maximum of u1 and i is ” ≪ max(u1, i) ≪ ’\n’; 1 The longest we have heard off was 18MB what corresponds to about 9000 pages of text.

4.2. GENERIC FUNCTIONS 93 Then the arguments are converted to int. 2 After these less pleasant details on templates one really good news: template functions perform as efficient as their non-templated counterpart! The reason is that C ++ generates new code for every type or type combination that the function is called with. Java in contrast compiles templates only once and executes them for different types by casting them to the corresponding types. This results in faster compilation and shorter executables but it is less efficient than non-templated implementations (which are already less efficient than C ++ programs). Another price we have to pay for the fast templates is that we have longer executables because of the multiple instantiations for each type (combination). However, in practice the number of a function’s instances will not be that large and it only really matters for non-inline functions with long implementations (including called template functions). Inline functions’ binary codes are at any rate inserted directly in the exutable at the location of the function call so that the impact on the executable length is the same for template and non-template functions. 4.2.1 The function accumulate TODO: An example on containers is much better than with ugly pointer arithmetic. Consider an array double a[n] which is described by its begin and end pointers a and a + n respectively. 3 We create a function for the sum of an array of doubles. The loop over the array uses pointers as was explained in Section 2.9. Figure 4.1 shows the positions of the begin pointer a and the end pointer a+n that is directly past the end of the array. a ❄ ✲ a + n Figure 4.1: An array of length n with begin and end pointers Thus, we specify the range of entries by an right-open interval of adresses. 2 For complicated reasons of compiler internals the explicit type parameter turns off argument-dependent name lookup (ADL). 3 An array and a pointer are treated in much the same way in C/C ++. So one can pass an array when a pointer is expected and it takes the address of the first entry &a[0]. a + n is for a pointer or array a and an integer n equivalent to &a[n]. ❄

4.2. GENERIC FUNCTIONS 93<br />

Then the arguments are converted to int. 2<br />

After these less pleasant details on templates one really good news: template functions per<strong>for</strong>m<br />

as efficient as their non-templated counterpart! The reason is that C ++ generates new code<br />

<strong>for</strong> every type or type combination that the function is called with. Java in contrast compiles<br />

templates only once and executes them <strong>for</strong> different types by casting them to the corresponding<br />

types. This results in faster compilation and shorter executables but it is less efficient than<br />

non-templated implementations (which are already less efficient than C ++ programs).<br />

Another price we have to pay <strong>for</strong> the fast templates is that we have longer executables because<br />

of the multiple instantiations <strong>for</strong> each type (combination). However, in practice the number of<br />

a function’s instances will not be that large and it only really matters <strong>for</strong> non-inline functions<br />

with long implementations (including called template functions). Inline functions’ binary codes<br />

are at any rate inserted directly in the exutable at the location of the function call so that the<br />

impact on the executable length is the same <strong>for</strong> template and non-template functions.<br />

4.2.1 The function accumulate<br />

TODO: An example on containers is much better than with ugly pointer arithmetic.<br />

Consider an array double a[n] which is described by its begin and end pointers a and a + n<br />

respectively. 3<br />

We create a function <strong>for</strong> the sum of an array of doubles. The loop over the array uses pointers<br />

as was explained in Section 2.9. Figure 4.1 shows the positions of the begin pointer a and the<br />

end pointer a+n that is directly past the end of the array.<br />

a<br />

❄<br />

✲<br />

a + n<br />

Figure 4.1: An array of length n with begin and end pointers<br />

Thus, we specify the range of entries by an right-open interval of adresses.<br />

2<br />

For complicated reasons of compiler internals the explicit type parameter turns off argument-dependent<br />

name lookup (ADL).<br />

3<br />

An array and a pointer are treated in much the same way in C/C ++. So one can pass an array when a<br />

pointer is expected and it takes the address of the first entry &a[0]. a + n is <strong>for</strong> a pointer or array a and an<br />

integer n equivalent to &a[n].<br />

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

Saved successfully!

Ooh no, something went wrong!