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

106 CHAPTER 4. GENERIC PROGRAMMING template class vector { ... }; That will do the trick. Pay attention to put a space between the closing ‘¿’; otherwise the compiler will take two subsequent ‘¿’ as shift operator ‘¿¿’ and becomes pretty confused. 14 This also works for classes with multiple parameters, for instance: template class vector { ... }; We can also specialize for all pointers: template class vector { ... }; Whenever the set of types is expressible by a Type Pattern we can apply partial specialization on it. Partial template specialization can be combined with regular template specialization from § 4.6.1 — let us call it ‘Complete Specialization’ for distinction. In this case, the complete specialization is prioritized over the partial one. Between different partial specializations the most specific is selected. In the following example: template class vector { ... }; template class vector { ... }; the second specialization is more specific than the first one and picked when matches. In this sense a complete specialization is always more specific than a partial one. 4.6.4 Partially Specializing Functions The C ++ standard committee distinguishes between explicit specialization as in the first paragraph of § 4.6.2 and implicit specialization. An example for implicit specialization is the following computation of a value’s magnitude: 14 In the next (new depending on publication date) standard, closing ‘¿’ without intermediate spaces. Some compilers — e.g., VS 2008 already support the conglutinated notation today.

4.6. TEMPLATE SPECIALIZATION 107 template T inline abs(const T& x) { return x < T(0) ? −x : x; } template // Do not specialize functions like this either T inline abs(const std::complex& x) { return sqrt(real(x)∗real(x) + imag(x)∗imag(x)); } This works significantly better than the explicit specialization but even this form of specialization fails sometimes in the sense that a template function is selected which is not the most specific. 15 A mean aspect of this implicit specialization is that it seems to work properly with few specializations and when a software project grows eventually it goes wrong. Since the developers have seen the specialization working before, they might not expect it and the unintended function selection might remain unobserved while corrupting results or at least wasting resources. It is also possible that the specialization behavior varies from compiler to compiler. 16 The only conclusion from this is to not specializing function templates! It introduces an unnecessary fragility into our software. Instead we introduce an additional class (called functor § 4.8) with an operator(). Template classes are properly specialized on all compilers 17 both partially and completely. In our abs example we start with the function itself and a forward declaration of the template class: template struct abs functor; template typename abs functor::result type inline abs(const T& x) { abs functor functor object; return functor object(x); } Alternatively to the forward declaration we could have declared the class directly. The return type of our function refers to a typedef or (as correct term in generic programming) to a ‘Associated Type’ of abs functor. Already for complex numbers we do not return the argument type itself but its associated type value type. Using an associated type here gives us all possible flexibility for further specialization. For instance, the magnitude of a vector could be the sum or maximum of the elements’ magnitudes or a vector with the magnitudes of each element. Evidently the functor classes must define a result type to be called. Inside the function, we instantiate the functor class with the argument type: abs functor and create an object of this type. Then we call the object’s application operator. As we do not 15 TODO: Good example. 16 TODO: Ask a compiler expert about this. 17 Several years ago many compilers failed in partial specialization, e.g. VS 2003, but today all major compiler handle this properly. If you nevertheless experience problems with this feature in some compiler take your hands off of it, most likely you will encounter further problems. Even the CUDA compiler that is far from being standard-compliant supports partial specialization.

4.6. TEMPLATE SPECIALIZATION 107<br />

template <br />

T inline abs(const T& x)<br />

{<br />

return x < T(0) ? −x : x;<br />

}<br />

template // Do not specialize functions like this either<br />

T inline abs(const std::complex& x)<br />

{<br />

return sqrt(real(x)∗real(x) + imag(x)∗imag(x));<br />

}<br />

This works significantly better than the explicit specialization but even this <strong>for</strong>m of specialization<br />

fails sometimes in the sense that a template function is selected which is not the most<br />

specific. 15 A mean aspect of this implicit specialization is that it seems to work properly with<br />

few specializations and when a software project grows eventually it goes wrong. Since the<br />

developers have seen the specialization working be<strong>for</strong>e, they might not expect it and the unintended<br />

function selection might remain unobserved while corrupting results or at least wasting<br />

resources. It is also possible that the specialization behavior varies from compiler to compiler. 16<br />

The only conclusion from this is to not specializing function templates! It introduces an<br />

unnecessary fragility into our software. Instead we introduce an additional class (called functor<br />

§ 4.8) with an operator(). Template classes are properly specialized on all compilers 17 both<br />

partially and completely.<br />

In our abs example we start with the function itself and a <strong>for</strong>ward declaration of the template<br />

class:<br />

template struct abs functor;<br />

template <br />

typename abs functor::result type<br />

inline abs(const T& x)<br />

{<br />

abs functor functor object;<br />

return functor object(x);<br />

}<br />

Alternatively to the <strong>for</strong>ward declaration we could have declared the class directly. The return<br />

type of our function refers to a typedef or (as correct term in generic programming) to a<br />

‘Associated Type’ of abs functor. Already <strong>for</strong> complex numbers we do not return the argument<br />

type itself but its associated type value type. Using an associated type here gives us all possible<br />

flexibility <strong>for</strong> further specialization. For instance, the magnitude of a vector could be the sum<br />

or maximum of the elements’ magnitudes or a vector with the magnitudes of each element.<br />

Evidently the functor classes must define a result type to be called.<br />

Inside the function, we instantiate the functor class with the argument type: abs functor<br />

and create an object of this type. Then we call the object’s application operator. As we do not<br />

15 TODO: Good example.<br />

16 TODO: Ask a compiler expert about this.<br />

17 Several years ago many compilers failed in partial specialization, e.g. VS 2003, but today all major compiler<br />

handle this properly. If you nevertheless experience problems with this feature in some compiler take your hands<br />

off of it, most likely you will encounter further problems. Even the CUDA compiler that is far from being<br />

standard-compliant supports partial specialization.

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

Saved successfully!

Ooh no, something went wrong!