Das neutrale Element automatisch berechnen
Für die Fälle, in denen es ein eindeutiges neutrales Element (bzgl. der Addition)
zum Wertetyp von Iterator
gibt,
können wir aber eine Vereinfachung anbieten,
die gar kein init
mehr braucht.
Dazu müssen wir neben dem Wertetyp auch noch den Wert des neutralen Elements
aus Iterator
ableiten.
Hier können wir analog zu
value_type
eine Typ-Abbildung neutral_element_addition
definieren, die jetzt aber nicht Typ auf Typ, sondern Typ auf Wert abbildet.
Sinnvollerweise ist neutral_element_addition
nicht auf einem Iterator-Typ,
sondern auf dem zugehörigen Werte-Typ definiert:
template<typename T>
struct neutral_element_addition
{ static T value() { return T(0); } };
template<>
struct neutral_element_addition<std::string>
{ static std::string value() { return std::string(""); } };
Momementan haben wir nur für std::string
eine Ausnahme (via Template-Spezialisierung) implementiert.
Für Ihre eigene, blitzschnelle 3D-Vektor-Klasse vec3
müssen Sie dann ebenfalls ein solche Spezialisierung implementieren,
falls vec3(0)
nicht funktioniert:
template<>
struct neutral_element_addition<vec3>
{ static vec3 value() { return vec3(0.0,0.0,0.0); } };
Summe mit automatischem init
In der neuen Implementierung von sum2
holen wir uns dann das neutrale Element über
neutral_element_addition
,
wobei wir im Code der Übersichtlichkeit halber
einen typedef
für den Werte-Typ spendieren:
template<typename Iterator>
typename value_type<Iterator>::result
sum2b(Iterator a, Iterator a_end)
{
typedef typename value_type<Iterator>::result value_t;
value_t res = neutral_element_addition<value_t>::value();
for(; a != a_end; ++a)
res += *a;
return res;
}
Diese Variante kann parallel zu der
allgemeineren Version
mit explizitem init
angeboten werden.
Damit funktionieren dann endlich unsere Beispiele
für string
und float
ohne irgendwelche Gymnastik:
float a[] = { 0.0, 0.5, 1.0, 1.5 };
float suma = sum2b(a, a+4); // beware of long sequences!
std::string words[] = { "This", "is", "a", "sentence" };
std::string sentence = sum2b(words, words+4);
Enge Wertetypen schlagen zurück
... nur dass die Ausgabe bei strings immer noch nicht gefällt. Außerdem begegnet uns ein altes Problem wieder: Mit der neuen Version, die das init-Argument automatisch berechnet, ist es viel einfacher, in die Falle eines zu engen Wertetyps zu tappen:
vector<unsigned char> dice_rolls(BigN);
// surprise: unsigned char used for summing
unsigned sum_dice_rolls = sum2b(dice_rolls.begin(), dice_rolls.end());
Wäre nur die Version
(sum3
)
mit explizitem init
Parameter verfügbar,
dann hätten wir wenigstens eine sichtbare, explizite Angabe
des (möglicherweise ungeeigneten) Wertetyps beim Aufruf erzwungen
(und eine gewisse Chance, dass der Programmierer das richtige tut).
Es ist eine sehr gute Übung, über Lösungen dieses Konflikts
zwischen Sicherheit und Bequemlichkeit nachzudenken
(auch und grade weil Sie damit den Rahmen diese Tutorials verlassen ...).