C++ for Scientists - Technische Universität Dresden
C++ for Scientists - Technische Universität Dresden C++ for Scientists - Technische Universität Dresden
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.
- Page 56 and 57: 56 CHAPTER 2. C++ BASICS A function
- Page 58 and 59: 58 CHAPTER 2. C++ BASICS Now that t
- Page 60 and 61: 60 CHAPTER 2. C++ BASICS we need sm
- Page 62 and 63: 62 CHAPTER 2. C++ BASICS 2.12 Exerc
- Page 64 and 65: 64 CHAPTER 2. C++ BASICS 2.13 Opera
- Page 66 and 67: 66 CHAPTER 3. CLASSES apply symm bl
- Page 68 and 69: 68 CHAPTER 3. CLASSES int main() {
- Page 70 and 71: 70 CHAPTER 3. CLASSES class solver
- Page 72 and 73: 72 CHAPTER 3. CLASSES • Compilers
- Page 74 and 75: 74 CHAPTER 3. CLASSES a real number
- Page 76 and 77: 76 CHAPTER 3. CLASSES } return ∗t
- Page 78 and 79: 78 CHAPTER 3. CLASSES This mechanis
- Page 80 and 81: 80 CHAPTER 3. CLASSES One could not
- Page 82 and 83: 82 CHAPTER 3. CLASSES class matrix
- Page 84 and 85: 84 CHAPTER 3. CLASSES Approach 3: R
- Page 86 and 87: 86 CHAPTER 3. CLASSES of the decrem
- Page 88 and 89: 88 CHAPTER 3. CLASSES There are two
- Page 90 and 91: 90 CHAPTER 4. GENERIC PROGRAMMING }
- Page 92 and 93: 92 CHAPTER 4. GENERIC PROGRAMMING c
- Page 94 and 95: 94 CHAPTER 4. GENERIC PROGRAMMING A
- Page 96 and 97: 96 CHAPTER 4. GENERIC PROGRAMMING v
- Page 98 and 99: 98 CHAPTER 4. GENERIC PROGRAMMING c
- Page 100 and 101: 100 CHAPTER 4. GENERIC PROGRAMMING
- Page 102 and 103: 102 CHAPTER 4. GENERIC PROGRAMMING
- Page 104 and 105: 104 CHAPTER 4. GENERIC PROGRAMMING
- Page 108 and 109: 108 CHAPTER 4. GENERIC PROGRAMMING
- Page 110 and 111: 110 CHAPTER 4. GENERIC PROGRAMMING
- Page 112 and 113: 112 CHAPTER 4. GENERIC PROGRAMMING
- Page 114 and 115: 114 CHAPTER 4. GENERIC PROGRAMMING
- Page 116 and 117: 116 CHAPTER 4. GENERIC PROGRAMMING
- Page 118 and 119: 118 CHAPTER 4. GENERIC PROGRAMMING
- Page 120 and 121: 120 CHAPTER 4. GENERIC PROGRAMMING
- Page 122 and 123: 122 CHAPTER 4. GENERIC PROGRAMMING
- Page 124 and 125: 124 CHAPTER 4. GENERIC PROGRAMMING
- Page 126 and 127: 126 CHAPTER 4. GENERIC PROGRAMMING
- Page 128 and 129: 128 CHAPTER 4. GENERIC PROGRAMMING
- Page 130 and 131: 130 CHAPTER 4. GENERIC PROGRAMMING
- Page 132 and 133: 132 CHAPTER 4. GENERIC PROGRAMMING
- Page 134 and 135: 134 CHAPTER 5. META-PROGRAMMING exp
- Page 136 and 137: 136 CHAPTER 5. META-PROGRAMMING dou
- Page 138 and 139: 138 CHAPTER 5. META-PROGRAMMING We
- Page 140 and 141: 140 CHAPTER 5. META-PROGRAMMING Fir
- Page 142 and 143: 142 CHAPTER 5. META-PROGRAMMING hig
- Page 144 and 145: 144 CHAPTER 5. META-PROGRAMMING The
- Page 146 and 147: 146 CHAPTER 5. META-PROGRAMMING tra
- Page 148 and 149: 148 CHAPTER 5. META-PROGRAMMING tem
- Page 150 and 151: 150 CHAPTER 5. META-PROGRAMMING 5.3
- Page 152 and 153: 152 CHAPTER 5. META-PROGRAMMING •
- Page 154 and 155: 154 CHAPTER 5. META-PROGRAMMING Dis
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.